[
  {
    "path": ".gitignore",
    "content": "*.pyc\n.idea/\n.DS_Store\n.python-version\nupload/\n*.log\nnode_modules/\npackage-lock.json\n.vscode/\nvenv/\n*.db\n"
  },
  {
    "path": "500.html",
    "content": "<html>\n<head>\n<style type=\"text/css\">\n*{\n  padding: 0;\n  margin: 0;\n  box-sizing: border-box;\n}\n\n*::before,\n*::after {\n    content: '';\n  position: absolute;\n}\n\nbody{\n\tbackground: #1B0034;\n\tbackground-image: linear-gradient( 135deg, #1B0034 10%, #33265C 100%);\n\tbackground-attachment: fixed;\n\tbackground-size: cover;\n\n}\n\n.error {\n\twidth: 100%;\n\theight: auto;\n\tmargin: 50px auto;\n\ttext-align: center;\n\tmargin-bottom: 0;\n}\n\n.dracula{\n\twidth: 230px;\n\theight: 250px;\n\tdisplay: inline-block;\n\tmargin: auto;\n\toverflowX: hidden;\n}\n\n.error .p {\n\theight: 50%;\n\tcolor: #c4ddc0;\n\tfont-size: 280px;\n\tmargin: 50px;\n\tdisplay: inline-block;\n}\n\n\n\n\n\n\n.con {\n  width: 500px;\n  height: 500px;\n  position: relative;\n  margin: 9% auto 0;\nanimation: ani9 0.3s ease-in-out infinite  alternate ;}\n\n@keyframes ani9 {\n    0%{\n    transform: translateY(10px);\n  }\n\n  100%{\n    transform: translateY(30px);\n  }\n\n}\n\n\n.con > * {\n  position: absolute;\n  top: 0; left: 0;\n}\n\n.hair{\n  top: -20px;\n  width: 210px;\n  height: 200px;\n  background: #C0D7DD;\n  border-radius: 0 50% 0 50%;\n  transform: rotate(45deg);\n  background: #33265C;\n}\n.hair-r{\n  left: 20px;\n  width: 210px;\n  height: 200px;\n  background: #C0D7DD;\n  border-radius: 0 50% 0 50%;\n  transform: rotate(45deg);\n  background: #33265C;\n\n}\n.head {\n  width: 200px;\n  height: 200px;\n  background: #C0D7DD;\n  border-radius: 0 50% 0 50%;\n  transform: rotate(45deg);\n}\n.eye {\n width: 20px; height:20px;\n  background: #111113;\n  border-radius: 50%;\n  top: 15%; left: 11.5%;\n  transition: .3s linear;\n}\n.eye-r{left: 24%;}\n\n.mouth {\n  width: 60px;\n  height: 20px;\n  background: #840021;\n  top: 20%;\n  left: 14%;\n  border-radius: 50% / 0 0 100% 100%;\n}\n.mouth::after{\n\n  border-left: 5px solid transparent;\n  border-right: 5px solid transparent;\n  border-top: 13px solid #FFFFFF;\n  left: 10px;\n\n}\n.mouth::before{\n  border-left: 5px solid transparent;\n  border-right: 5px solid transparent;\n  border-top: 13px solid #FFFFFF;\n  left: 40px;\n}\n\n.blod {\n  width: 8px;\n  height: 20px;\n  background: #840021;\n  top: 23%; left: 17%;\n  border-radius: 20px;\n}\n.blod::after{\n   width: 2px;\n  height: 10px;\n  background: #FFF;\n  top: 20%; left: 10%;\n  border-radius: 20px;\n\n}\n.blod2 {\n  top: 23%; left: 20%;\n  width: 13px;\n  height: 13px;\n  border-radius: 50% 50% 50% 0;\n  transform: rotate(130deg);\n  animation: blod 2s linear infinite;\n  opacity: 0;\n}\n@keyframes blod {\n  0%   {opacity: 1;}\n  100%   {background:red; opacity: 0; top:50%;}\n\n\n}\n\n\n\n/* page-ms */\n.page-ms {transform: translateY(-50px);}\n\n.error p.page-msg {\n\ttext-align: center;\n\tcolor: #ddc0c0;\n\tfont-size: 30px;\n\tfont-family: 'Combo', cursive;\n\tmargin-top: 20px;\n}\nbutton.go-back {\n\t\tfont-size: 30px;\n    font-family: 'Combo', cursive;\n    border: none;\n    padding: 10px 20px;\n    cursor: pointer;\n    transition: 0.3s linear;\n    z-index: 9;\n    border-radius: 10px;\n    background: #c0c7ddad;\n    color: #26eff1;\n    box-shadow: 0 0 10px 0 #C0D7DD;\n}\nbutton:hover {box-shadow: 0 0 20px 0 #C0D7DD;}\n\naudio {\n/* \tdisplay: none; */\n}\n</style>\n</head>\n<body>\n<div class=\"container\">\n\n\t<div  class=\"error\">\n\t\t<p class=\"p\">5</p>\n\t\t<span class=\"dracula\">\n\t\t\t<div class=\"con\">\n\t\t\t\t<div class=\"hair\"></div>\n\t\t\t\t<div class=\"hair-r\"></div>\n\t\t\t\t<div class=\"head\"></div>\n    \t\t<div class=\"eye\"></div>\n    \t\t<div class=\"eye eye-r\"></div>\n  \t\t\t<div class=\"mouth\"></div>\n  \t\t\t<div class=\"blod\"></div>\n  \t\t\t<div class=\"blod blod2\"></div>\n\t\t\t</div>\n\t\t</span>\n\t\t<p class=\"p\">0</p>\n\n\t\t<div class=\"page-ms\">\n\t\t\t\t\t<button class=\"go-back\">Oops</button>\n\t\t\t<p class=\"page-msg\"> 网站正在更新维护中，请稍后2分钟再试! </p>\n\t\t</div>\n</div>\n\n\t</div>\n\t</body>\n</html>"
  },
  {
    "path": "README.md",
    "content": "# django + vue 工作流管理系统\n包含 `用户`、`角色`、`菜单`、`权限` 管理， 这是基础的工作流系统，初始化会生成请假工作流， 也可以自行配置其他工作流比如，发布工单等。\n\n[comment]: <> (- 后端model参考: [loonflow]&#40;https://github.com/blackholll/loonflow&#41;, 非常不错的一个项目)\n[comment]: <> (- 前端设计参考: [花裤衩 vue-element-admin]&#40;https://github.com/PanJiaChen/vue-element-admin&#41;, 大神作品没得说)\n## 开发环境\n### 后端\n安装依赖\n```bash\ncd backend\npip install -r dev_requirements.txt\n```\n\n初始化系统\n- 生成管理员账号 `admin 123456`\n```bash\npython manage.py migrate\npython manage.py init_sys\n```\n\n生成工作流\n- 用户 `ops`,`ops_tl`,`dev`,`dev_tl`,`hr`,`hr_tl`\n- 密码 `123456`\n\n```bash\npython manage.py init_wf\npython manage.py init_ticket\npython manage.py init_leave\n```\n\n运行\n```bash\npython manage.py runserver\n```\n\n### 前端\n```bash\ncd frontend\nnpm install\nnpm run dev\n```\n\n## 开始使用\n使用 `admin` 登录\n### 给所有角色分配工作流权限\n![role](https://github.com/itimor/one-workflow/raw/master/gifs/role.png)\n\n### 分配菜单 和 数据 权限\n![role_edit](https://github.com/itimor/one-workflow/raw/master/gifs/role_edit.png)\n\n### 配置假期工作流\n![role](https://github.com/itimor/one-workflow/raw/master/gifs/leave.png)\n\n### 新建工单\n![role](https://github.com/itimor/one-workflow/raw/master/gifs/new.png)\n\n### 编辑工单\n![role](https://github.com/itimor/one-workflow/raw/master/gifs/edit.png)\n\n### 所有工单\n![role](https://github.com/itimor/one-workflow/raw/master/gifs/all.png)"
  },
  {
    "path": "backend/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\n"
  },
  {
    "path": "backend/celery.service",
    "content": "[Unit]\nDescription=start celery worker\n\n[Service]\nExecStart=/bin/bash -c 'cd /opt/projects/one-oms/backend; /root/.pyenv/versions/envoms/bin/celery -A core worker -B --loglevel=info -f /data/logs/celery.log'\n#非正常dead，自动重启\nRestart=on-failure\n#3秒后启动\nRestartSec=3s\nKillSignal=SIGQUIT\nType=simple\nNotifyAccess=all\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "backend/common/JSONRenderer.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom rest_framework.renderers import JSONRenderer\n\n\nclass CustomJSONRenderer(JSONRenderer):\n    def render(self, data, accepted_media_type=None, renderer_context=None):\n        response_data = {}\n        object_list = 'results'\n        try:\n            meta_dict = getattr(renderer_context.get('view').get_serializer().Meta, 'meta_dict')\n        except:\n            meta_dict = dict()\n\n        try:\n            data.get('paginated_results')\n            response_data['meta'] = data['meta']\n            response_data[object_list] = data['results']\n        except:\n            response_data[object_list] = data\n            response_data['meta'] = dict()\n            response_data['meta'].update(meta_dict)\n\n        response = super(CustomJSONRenderer, self).render(response_data, accepted_media_type, renderer_context)\n        return response\n"
  },
  {
    "path": "backend/common/__init__.py",
    "content": ""
  },
  {
    "path": "backend/common/dispath.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nimport six\nfrom rest_framework.serializers import Serializer\nfrom rest_framework.response import Response\n\n\nclass JsonResponse(Response):\n    \"\"\"\n    An HttpResponse that allows its data to be rendered into\n    arbitrary media types.\n    \"\"\"\n\n    def __init__(self, data=None, code=None, desc=None,\n                 status=None,\n                 template_name=None, headers=None,\n                 exception=False, content_type=None):\n        \"\"\"\n        Alters the init arguments slightly.\n        For example, drop 'template_name', and instead use 'data'.\n        Setting 'renderer' and 'media_type' will typically be deferred,\n        For example being set automatically by the `APIView`.\n        \"\"\"\n        super().__init__(None, status=status)\n\n        if isinstance(data, Serializer):\n            msg = (\n                'You passed a Serializer instance as data, but '\n                'probably meant to pass serialized `.data` or '\n                '`.error`. representation.'\n            )\n            raise AssertionError(msg)\n\n        self.data = data\n        self.template_name = template_name\n        self.exception = exception\n        self.content_type = content_type\n\n        if headers:\n            for name, value in six.iteritems(headers):\n                self[name] = value\n"
  },
  {
    "path": "backend/common/django.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom django.utils.deprecation import MiddlewareMixin\n\nclass DisableCSRF(MiddlewareMixin):\n    def process_request(self, request):\n        setattr(request, '_dont_enforce_csrf_checks', True)\n"
  },
  {
    "path": "backend/common/exceptions.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom rest_framework.views import exception_handler\nfrom common import status\n\n\ndef JSONExceptionHandler(exc, context):\n    response = exception_handler(exc, context)\n\n    if response is not None:\n        resp = {\n            'code': status.HTTP_400_BAD_REQUEST,\n            'result': dict(response.data)\n        }\n        response.data = resp\n\n    return response\n\n\nclass ExceptionX_Result:\n    exceptionType = None\n    exceptionTitle = None\n    exceptionDetail = None\n\n\nclass ExceptionX:\n\n    @staticmethod\n    def ToString(e):\n        result = ExceptionX_Result\n        tempStr = str(type(e))\n        tempStrArray = tempStr.split(\"'\")\n        result.exceptionTitle = tempStrArray[1]\n        result.exceptionType = tempStrArray[0][1:]\n        result.exceptionDetail = str(e)\n\n        if result.exceptionDetail[0] == \"<\":\n            if result.exceptionDetail[result.exceptionDetail.__len__() - 1] == \">\":\n                result.exceptionDetail = result.exceptionDetail[1:result.exceptionDetail.__len__() - 1]\n        return result\n\n    @staticmethod\n    def PasreRaise(e):\n        result = ExceptionX_Result\n        tempStr = str(type(e))\n        tempStrArray = tempStr.split(\"'\")\n        result.exceptionTitle = tempStrArray[1]\n        result.exceptionType = tempStrArray[0][1:]\n        result.exceptionDetail = str(e)\n\n        if result.exceptionDetail[0] == \"<\":\n            if result.exceptionDetail[result.exceptionDetail.__len__() - 1] == \">\":\n                result.exceptionDetail = result.exceptionDetail[1:result.exceptionDetail.__len__() - 1]\n        return result.exceptionDetail\n"
  },
  {
    "path": "backend/common/models.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom django.db import models\n\n\nclass BaseModel(models.Model):\n    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')\n    update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')\n    memo = models.TextField(blank=True, verbose_name='备注')\n\n    class Meta:\n        ordering = ['-create_time']\n        abstract = True\n\n"
  },
  {
    "path": "backend/common/pagination.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom collections import OrderedDict\n\nfrom rest_framework.pagination import PageNumberPagination, LimitOffsetPagination\n\nfrom common import status\nfrom common.dispath import JsonResponse\n\n\ndef _positive_int(integer_string, strict=False, cutoff=None):\n    \"\"\"\n    分页大小为零不分页\n    \"\"\"\n    ret = int(integer_string)\n    if ret < 0:\n        raise ValueError()\n    if (ret == 0) and strict:\n        return None\n    if cutoff:\n        return min(ret, cutoff)\n    return ret\n\n\nclass StandardResultsSetPagination(PageNumberPagination):\n    \"\"\"\n    配置分页规则\n    \"\"\"\n    page_size = 20\n    page_size_query_param = 'limit'\n    page_query_param = 'page'\n    max_page_size = 1000\n\n    def get_paginated_response(self, data):\n        return JsonResponse(OrderedDict([\n            ('count', self.page.paginator.count),\n            ('next', self.get_next_link()),\n            ('previous', self.get_previous_link()),\n            ('results', data)\n        ], code=status.HTTP_200_OK))\n\n    def get_page_size(self, request):\n        if self.page_size_query_param:\n            try:\n                return _positive_int(\n                    request.query_params[self.page_size_query_param],\n                    strict=True,\n                    cutoff=self.max_page_size\n                )\n            except (KeyError, ValueError):\n                return None\n        return self.page_size\n\n\nclass CustomLimitOffsetPagination(LimitOffsetPagination):\n    def get_offset(self, request):\n        try:\n            return (int(request.query_params['offset']) - 1) * int(request.query_params['limit'])\n        except (KeyError, ValueError):\n            return 1\n"
  },
  {
    "path": "backend/common/status.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom __future__ import unicode_literals\n\n\ndef is_informational(code):\n    return 100 <= code <= 199\n\n\ndef is_success(code):\n    return 200 <= code <= 299\n\n\ndef is_redirect(code):\n    return 300 <= code <= 399\n\n\ndef is_client_error(code):\n    return 400 <= code <= 499\n\n\ndef is_server_error(code):\n    return 500 <= code <= 599\n\n\nHTTP_100_CONTINUE = 10000\nHTTP_101_SWITCHING_PROTOCOLS = 10100\nHTTP_200_OK = 20000\nHTTP_201_CREATED = 20100\nHTTP_202_ACCEPTED = 20200\nHTTP_203_NON_AUTHORITATIVE_INFORMATION = 20300\nHTTP_204_NO_CONTENT = 20400\nHTTP_205_RESET_CONTENT = 20500\nHTTP_206_PARTIAL_CONTENT = 20600\nHTTP_207_MULTI_STATUS = 20700\nHTTP_300_MULTIPLE_CHOICES = 30000\nHTTP_301_MOVED_PERMANENTLY = 30100\nHTTP_302_FOUND = 30200\nHTTP_303_SEE_OTHER = 30300\nHTTP_304_NOT_MODIFIED = 30400\nHTTP_305_USE_PROXY = 30500\nHTTP_306_RESERVED = 30600\nHTTP_307_TEMPORARY_REDIRECT = 30700\nHTTP_400_BAD_REQUEST = 40000\nHTTP_401_UNAUTHORIZED = 40100\nHTTP_402_PAYMENT_REQUIRED = 40200\nHTTP_403_FORBIDDEN = 40300\nHTTP_404_NOT_FOUND = 40400\nHTTP_405_METHOD_NOT_ALLOWED = 40500\nHTTP_406_NOT_ACCEPTABLE = 40600\nHTTP_407_PROXY_AUTHENTICATION_REQUIRED = 40700\nHTTP_408_REQUEST_TIMEOUT = 40800\nHTTP_409_CONFLICT = 40900\nHTTP_410_GONE = 41000\nHTTP_411_LENGTH_REQUIRED = 41100\nHTTP_412_PRECONDITION_FAILED = 41200\nHTTP_413_REQUEST_ENTITY_TOO_LARGE = 41300\nHTTP_414_REQUEST_URI_TOO_LONG = 41400\nHTTP_415_UNSUPPORTED_MEDIA_TYPE = 41500\nHTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 41600\nHTTP_417_EXPECTATION_FAILED = 41700\nHTTP_422_UNPROCESSABLE_ENTITY = 42200\nHTTP_423_LOCKED = 42300\nHTTP_424_FAILED_DEPENDENCY = 42400\nHTTP_428_PRECONDITION_REQUIRED = 42800\nHTTP_429_TOO_MANY_REQUESTS = 42900\nHTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 43100\nHTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS = 45100\nHTTP_500_INTERNAL_SERVER_ERROR = 50000\nHTTP_501_NOT_IMPLEMENTED = 50100\nHTTP_502_BAD_GATEWAY = 50200\nHTTP_503_SERVICE_UNAVAILABLE = 50300\nHTTP_504_GATEWAY_TIMEOUT = 50400\nHTTP_505_HTTP_VERSION_NOT_SUPPORTED = 50500\nHTTP_507_INSUFFICIENT_STORAGE = 50700\nHTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 51100\n"
  },
  {
    "path": "backend/common/views.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom __future__ import print_function, unicode_literals\n\nimport json\nfrom rest_framework.response import Response\nfrom collections import OrderedDict\nfrom rest_framework import viewsets\nfrom django.utils import timezone\nfrom rest_framework.decorators import action\nfrom common import status\nfrom common.dispath import JsonResponse\nfrom common.exceptions import *\nfrom tools.models import RequestEvent\n\n\nclass ModelViewSet(viewsets.ModelViewSet):\n\n    def __init__(self, *args, **kwargs):\n        super(ModelViewSet, self).__init__(*args, **kwargs)\n        self.resultData = False\n\n    def watch_audit_log(self, request):\n\n        ip = request.META.get(\"HTTP_X_FORWARDED_FOR\", \"\")\n        if not ip:\n            ip = request.META.get('REMOTE_ADDR', \"\")\n        method = request._request.method\n        RequestEvent.objects.create(\n            url=request.path,\n            method=method,\n            query_string=json.dumps({\n                'query_params': request.query_params,\n                'json': request.data\n            }),\n            user=self.request.user,\n            remote_ip=ip,\n            create_time=timezone.now()\n        )\n\n    def create(self, request, *args, **kwargs):\n        self.watch_audit_log(request)\n        serializer = self.get_serializer(data=request.data)\n        serializer.is_valid(raise_exception=True)\n        try:\n            self.perform_create(serializer)\n            headers = self.get_success_headers(serializer.data)\n            return JsonResponse(OrderedDict([\n                ('results', serializer.data)\n            ], code=status.HTTP_200_OK), headers=headers)\n        except Exception as e:\n            print(e)\n            return JsonResponse(OrderedDict([\n                ('results', {\"msg\": ExceptionX.PasreRaise(e)})\n            ], code=status.HTTP_500_INTERNAL_SERVER_ERROR))\n\n    def perform_create(self, serializer):\n        serializer.save()\n\n    def list(self, request, *args, **kwargs):\n        # 不记录list get请求\n        # self.watch_audit_log(request)\n\n        queryset = self.filter_queryset(self.get_queryset())\n        page = self.paginate_queryset(queryset)\n        if page is not None:\n            serializer = self.get_serializer(page, many=True)\n            return self.get_paginated_response(serializer.data)\n\n        serializer = self.get_serializer(queryset, many=True)\n        return JsonResponse(OrderedDict([\n            ('results', serializer.data)\n        ], code=status.HTTP_200_OK))\n\n    def retrieve(self, request, *args, **kwargs):\n        self.watch_audit_log(request)\n        instance = self.get_object()\n        serializer = self.get_serializer(instance)\n        return JsonResponse(OrderedDict([\n            ('results', serializer.data)\n        ], code=status.HTTP_200_OK))\n\n    def update(self, request, *args, **kwargs):\n        self.watch_audit_log(request)\n        partial = kwargs.pop('partial', False)\n        instance = self.get_object()\n        serializer = self.get_serializer(instance, data=request.data, partial=partial)\n        serializer.is_valid(raise_exception=True)\n        self.perform_update(serializer)\n\n        if getattr(instance, '_prefetched_objects_cache', None):\n            instance._prefetched_objects_cache = {}\n\n        return JsonResponse(OrderedDict([\n            ('results', serializer.data)\n        ], code=status.HTTP_200_OK))\n\n    def perform_update(self, serializer):\n        serializer.is_valid(raise_exception=True)\n        serializer.save()\n\n    def partial_update(self, request, *args, **kwargs):\n        kwargs['partial'] = True\n        return self.update(request, *args, **kwargs)\n\n    def destroy(self, request, *args, **kwargs):\n        self.watch_audit_log(request)\n        instance = self.get_object()\n        self.perform_destroy(instance)\n        return JsonResponse(OrderedDict(code=status.HTTP_200_OK))\n\n    def perform_destroy(self, instance):\n        instance.delete()\n\n\nclass FKModelViewSet(ModelViewSet):\n\n    def transer(self, instance=None, id=None):\n        self.resultData = True\n        if self.action == \"create\":\n            self.kwargs = {'pk': id}\n            instance = self.get_object()\n        serializer = self.get_serializer(instance)\n        return serializer\n\n    def perform_create(self, serializer):\n        super(FKModelViewSet, self).perform_create(serializer)\n        self.readSerializer = self.transer(id=serializer.data['id'])\n\n    def perform_update(self, serializer):\n        super(FKModelViewSet, self).perform_update(serializer)\n        self.readSerializer = self.transer(self.readInstance)\n\n    def perform_destroy(self, instance):\n        super(FKModelViewSet, self).perform_destroy(instance)\n\n    def create(self, request, *args, **kwargs):\n        self.watch_audit_log(request)\n        serializer = self.get_serializer(data=request.data)\n        serializer.is_valid(raise_exception=True)\n        self.perform_create(serializer)\n        headers = self.get_success_headers(serializer.data)\n        serializer = self.readSerializer\n        return JsonResponse(OrderedDict([\n            ('results', serializer.data)\n        ], code=status.HTTP_200_OK), headers=headers)\n\n    def update(self, request, *args, **kwargs):\n        self.watch_audit_log(request)\n        partial = kwargs.pop('partial', False)\n        self.readInstance = instance = self.get_object()\n        serializer = self.get_serializer(instance, data=request.data, partial=partial)\n        serializer.is_valid(raise_exception=True)\n        self.perform_update(serializer)\n        serializer = self.readSerializer\n\n        if getattr(instance, '_prefetched_objects_cache', None):\n            instance._prefetched_objects_cache = {}\n\n        return JsonResponse(OrderedDict([\n            ('results', serializer.data)\n        ], code=status.HTTP_200_OK))\n\n    def destroy(self, request, *args, **kwargs):\n        self.watch_audit_log(request)\n        instance = self.get_object()\n        self.perform_destroy(instance)\n        return JsonResponse(OrderedDict(code=status.HTTP_200_OK))\n\n\n# 批量操作modelview  bulk_create|bulk_delete|bulk_update\nclass BulkModelMixin(ModelViewSet):\n    # 批量添加\n    @action(methods=['post'], url_path='bulk_create', detail=False)\n    def bulk_create(self, request, *args, **kwargs):\n        \"\"\"\n        /api/tool/simple/bulk_create/\n        :return:\n        \"\"\"\n        self.watch_audit_log(request)\n        objs = request.data\n\n        if not objs:\n            return Response(status=status.HTTP_400_BAD_REQUEST)\n\n        bulk_models = []\n        for obj in objs:\n            req = {'id': '0102', 'msg': 'success'}\n            print(obj)\n            try:\n                serializer = self.get_serializer(data=obj)\n                serializer.is_valid(raise_exception=True)\n                self.perform_create(serializer)\n                req['id'] = serializer.data['id']\n            except Exception as e:\n                req['msg'] = ExceptionX.ToString(e)\n\n            bulk_models.append(req)\n\n        return JsonResponse(OrderedDict([\n            ('results', bulk_models)\n        ], code=status.HTTP_200_OK))\n\n    @action(methods=['delete'], url_path='bulk_delete', detail=False)\n    def bulk_delete(self, request, *args, **kwargs):\n        \"\"\"\n        /api/tool/simple/bulk_delete/\n        :return:\n        \"\"\"\n        self.watch_audit_log(request)\n        ids = request.data\n\n        if not ids:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n\n        bulk_models = []\n        for id in ids:\n            req = {'id': id, 'msg': 'success'}\n            try:\n                queryset = self.filter_queryset(self.get_queryset())\n                instance = queryset.get(pk=id)\n                self.perform_destroy(instance)\n            except Exception as e:\n                req['msg'] = ExceptionX.ToString(e)\n\n            bulk_models.append(req)\n\n        return JsonResponse(OrderedDict([\n            ('results', bulk_models)\n        ], code=status.HTTP_200_OK))\n\n    @action(methods=['put', 'patch'], url_path='bulk_update', detail=False)\n    def bulk_update(self, request, *args, **kwargs):\n        \"\"\"\n        /api/tool/simple/bulk_update/\n        :return:\n        \"\"\"\n        self.watch_audit_log(request)\n        ids = request.data['ids']\n        obj = request.data['obj']\n\n        if not ids or not obj:\n            return Response(status=status.HTTP_400_BAD_REQUEST)\n\n        bulk_models = []\n        for id in ids:\n            req = {'id': id, 'msg': 'success'}\n            try:\n                queryset = self.filter_queryset(self.get_queryset())\n                instance = queryset.get(pk=id)\n                serializer = self.get_serializer(instance, data=obj, partial=True)\n                serializer.is_valid(raise_exception=True)\n                self.perform_update(serializer)\n            except Exception as e:\n                req['msg'] = ExceptionX.ToString(e)\n\n            bulk_models.append(req)\n\n        return JsonResponse(OrderedDict([\n            ('results', bulk_models)\n        ], code=status.HTTP_200_OK))\n"
  },
  {
    "path": "backend/core/__init__.py",
    "content": "from __future__ import absolute_import, unicode_literals\n# This will make sure the app is always imported when\n# Django starts so that shared_task will use this app.\n# import pymysql\n#\n# pymysql.install_as_MySQLdb()\n"
  },
  {
    "path": "backend/core/celery.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom __future__ import absolute_import, unicode_literals\nimport os\nfrom celery import Celery\n# set the default Django settings module for the 'celery' program.\nos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')\ncelery_app = Celery('core', result_backend='django-db')\n# Using a string here means the worker doesn't have to serialize\n# the configuration object to child processes.\n# - namespace='CELERY' means all celery-related configuration keys\n#   should have a `CELERY_` prefix.\ncelery_app.config_from_object('django.conf:settings', namespace='CELERY')\n# Load task modules from all registered Django app configs.\ncelery_app.autodiscover_tasks()\ncelery_app.loader.override_backends['django-db'] = 'django_celery_results.backends.database:DatabaseBackend'"
  },
  {
    "path": "backend/core/settings/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nimport platform\nfrom .base import *\n\nos_type = platform.system()\n\nif os_type == 'Windows':\n    print('进入 dev ')\n    from .dev import *\nelif os_type == 'Linux':\n    print('进入 prod ')\n    from .prod import *\nelse:\n    print('进入 mac')\n    from .mac import *\n"
  },
  {
    "path": "backend/core/settings/base.py",
    "content": "\"\"\"\nDjango settings for core project.\n\nGenerated by 'django-admin startproject' using Django 2.1.1.\n\nFor more information on this file, see\nhttps://docs.djangoproject.com/en/2.1/topics/settings/\n\nFor the full list of settings and their values, see\nhttps://docs.djangoproject.com/en/2.1/ref/settings/\n\"\"\"\n\nimport os\nimport datetime\nfrom logging.config import dictConfig\n\n# Build paths inside the project like this: os.path.join(BASE_DIR, ...)\nBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n\n# Application definition\n\nINSTALLED_APPS = [\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'rest_framework',  # restful api\n    'django_filters',  # 过滤\n    'corsheaders',  # 跨域\n    'common',\n    'tools',\n    'systems',\n    'workflows',\n    'tickets',\n    'notices',\n]\n\nMIDDLEWARE = [\n    'corsheaders.middleware.CorsMiddleware',\n    'django.middleware.security.SecurityMiddleware',\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.locale.LocaleMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n]\n\nROOT_URLCONF = 'core.urls'\n\nTEMPLATES = [\n    {\n        'BACKEND': 'django.template.backends.django.DjangoTemplates',\n        # 'DIRS': [],\n        'DIRS': ['../frontend/dist'],\n        'APP_DIRS': True,\n        'OPTIONS': {\n            'context_processors': [\n                'django.template.context_processors.debug',\n                'django.template.context_processors.request',\n                'django.contrib.auth.context_processors.auth',\n                'django.contrib.messages.context_processors.messages',\n            ],\n        },\n    },\n]\n\nWSGI_APPLICATION = 'core.wsgi.application'\n\n# Database\n# https://docs.djangoproject.com/en/1.11/ref/settings/#databases\n\n# Password validation\n# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators\n\nAUTH_PASSWORD_VALIDATORS = [\n    {\n        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',\n    },\n]\n\n# Internationalization\n# https://docs.djangoproject.com/en/2.1/topics/i18n/\n\nLANGUAGE_CODE = 'zh-hans'\nTIME_ZONE = 'Asia/Shanghai'\nUSE_I18N = True\nUSE_L10N = True\nUSE_TZ = True\n\n# Static files (CSS, JavaScript, Images)\n# https://docs.djangoproject.com/en/2.1/howto/static-files/\n\nSTATIC_URL = '/static/'\n# Add for vuejs\nSTATICFILES_DIRS = [\n    os.path.join(BASE_DIR, \"./templates\"),\n]\n\nMEDIA_ROOT = os.path.join(BASE_DIR, '../upload')\nMEDIA_URL = '/upload/'\n\nREST_USE_JWT = True\nREST_FRAMEWORK = {\n    'DATETIME_FORMAT': \"%Y-%m-%d %H:%M:%S\",\n    'DEFAULT_PAGINATION_CLASS': 'common.pagination.StandardResultsSetPagination',\n    'DEFAULT_PERMISSION_CLASSES': (\n        'rest_framework.permissions.AllowAny',\n        'rest_framework.permissions.IsAuthenticated',\n        'systems.permissions.IsOwnerRoles',\n    ),\n    'DEFAULT_FILTER_BACKENDS': (\n        'django_filters.rest_framework.DjangoFilterBackend',\n        'rest_framework.filters.SearchFilter',\n        'rest_framework.filters.OrderingFilter',\n    ),\n    'DEFAULT_AUTHENTICATION_CLASSES': (\n        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',\n        'rest_framework.authentication.BasicAuthentication',\n        'rest_framework.authentication.SessionAuthentication',\n    ),\n    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',\n    'DEFAULT_RENDERER_CLASSES': [\n        'rest_framework.renderers.JSONRenderer',\n        'rest_framework.renderers.BrowsableAPIRenderer',\n    ],\n}\n\nJWT_AUTH = {\n    'JWT_AUTH_HEADER_PREFIX': 'core',\n    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),\n    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=30),\n}\n\n# user model\nAUTH_USER_MODEL = \"systems.User\"\nSUPER_ADMIN_USER = 'admin'\n\nCORS_ORIGIN_ALLOW_ALL = True\n\nLOGGING = {\n    'version': 1,\n    'disable_existing_loggers': True,\n    'formatters': {\n        'simple': {\n            'format': '%(levelname)s %(asctime)s %(message)s',\n            'datefmt': '%y %b %d, %H:%M:%S',\n        },\n    },\n    'handlers': {\n        'console': {\n            'level': 'INFO',\n            'class': 'logging.StreamHandler',\n            'formatter': 'simple'\n        },\n    },\n    'loggers': {\n        'django': {\n            'handlers': ['console'],\n            'propagate': True,\n            'level': 'INFO',\n        },\n    }\n}\n\ndictConfig(LOGGING)\n"
  },
  {
    "path": "backend/core/settings/dev.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nimport os\n\nAPP_ENV = 'dev'\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = '64318ob@vbou7h50)b0a_pfda4d$bw2nhl4h*m$qo0_e_fxw=658!z*x'\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG = True\n\nALLOWED_HOSTS = ['*']\n\n# Build paths inside the project like this: os.path.join(BASE_DIR, ...)\nBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n\n# sqlite\nDATABASES = {\n    'default': {\n        'ENGINE': 'django.db.backends.sqlite3',\n        'NAME': os.path.join(BASE_DIR, '../core.db'),\n    }\n}\n\n# mysql\n# DATABASES = {\n#     'default': {\n#         'ENGINE': 'django.db.backends.mysql',\n#         'NAME': 'one',\n#         'USER': 'root',\n#         'PASSWORD': 'momo520',\n#         'HOST': '1.1.1.11',\n#         'OPTIONS': {\n#             \"init_command\": \"SET foreign_key_checks=0;\",\n#         }\n#     }\n# }\n\n# 加载 mysql\n# import pymysql\n# pymysql.install_as_MySQLdb()\n"
  },
  {
    "path": "backend/core/settings/mac.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nimport os\n\nAPP_ENV = 'dev'\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = '64318ob@vbou7h50)b0a_pfda4d$bw2nhl4h*m$qo0_e_fxw=658!z*x'\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG = True\n\nALLOWED_HOSTS = ['*']\n\n# Build paths inside the project like this: os.path.join(BASE_DIR, ...)\nBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n\n# sqlite\nDATABASES = {\n    'default': {\n        'ENGINE': 'django.db.backends.sqlite3',\n        'NAME': os.path.join(BASE_DIR, '../core.db'),\n    }\n}\n\n# mysql\n# DATABASES = {\n#     'default': {\n#         'ENGINE': 'django.db.backends.mysql',\n#         'NAME': 'one',\n#         'USER': 'root',\n#         'PASSWORD': 'momo520',\n#         'HOST': '1.1.1.11',\n#         'OPTIONS': {\n#             \"init_command\": \"SET foreign_key_checks=0;\",\n#         }\n#     }\n# }\n\n# 加载 mysql\n# import pymysql\n# pymysql.install_as_MySQLdb()\n"
  },
  {
    "path": "backend/core/settings/prod.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nimport os\n\nAPP_ENV = 'prod'\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = '64318ob@vbou7h50)b0a_pfda4d$bw2nhl4h*m$qo0_e_fxw=658!z*x'\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG = False\n\nALLOWED_HOSTS = ['*']\n\n# mysql\nDATABASES = {\n    'default': {\n        'ENGINE': 'django.db.backends.mysql',\n        'NAME': 'one',\n        'USER': 'root',\n        'PASSWORD': 'TY%pwd123',\n        'HOST': 'localhost',\n        'OPTIONS': {\n            \"init_command\": \"SET foreign_key_checks=0;\",\n        }\n    }\n}\n"
  },
  {
    "path": "backend/core/urls.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom django.conf.urls import url, include\nfrom django.conf.urls.static import static\nfrom django.views.generic.base import TemplateView\nfrom core import settings\n\nurlpatterns = static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + \\\n              [\n                  # 工具管理\n                  url(r'api/tool/', include(('tools.urls', 'tools'), namespace=\"tools\")),\n                  # 系统管理\n                  url(r'api/sys/', include(('systems.urls', 'systems'), namespace=\"systems\")),\n                  # 工作流管理\n                  url(r'api/workflow/', include(('workflows.urls', 'workflows'), namespace=\"workflows\")),\n                  # 工单管理\n                  url(r'api/ticket/', include(('tickets.urls', 'tickets'), namespace=\"tickets\")),\n                  # 通知管理\n                  url(r'api/notice/', include(('notices.urls', 'notices'), namespace=\"notices\")),\n              ]\n\nif settings.APP_ENV == 'prod':\n    from rest_framework.documentation import include_docs_urls\n\n    urlpatterns += [\n        # api文档\n        url(r'^docs/', include_docs_urls(title='X Document')),\n        # 静态模板\n        url(r'', TemplateView.as_view(template_name=\"index.html\")),\n    ]\nelse:\n    from django.contrib import admin\n\n    urlpatterns += [\n        # 管理后台\n        url(r'^admin/', admin.site.urls),\n    ]\n"
  },
  {
    "path": "backend/core/wsgi.py",
    "content": "\"\"\"\nWSGI config for core project.\n\nIt exposes the WSGI callable as a module-level variable named ``application``.\n\nFor more information on this file, see\nhttps://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/\n\"\"\"\n\nimport os\n\nfrom django.core.wsgi import get_wsgi_application\n\nos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')\n\napplication = get_wsgi_application()\n"
  },
  {
    "path": "backend/dev_requirements.txt",
    "content": "asgiref==3.2.3\ncertifi==2019.11.28\nchardet==3.0.4\ncoreapi==2.3.3\ncoreschema==0.0.4\nDjango==3.0.3\ndjango-cors-headers==3.2.1\ndjango-filter==2.2.0\ndjango-rest-auth==0.9.5\ndjangorestframework==3.11.0\ndjangorestframework-jwt==1.11.0\nidna==2.8\nIPy==1.0\nitypes==1.1.0\nJinja2==2.10.3\nMarkupSafe==1.1.1\nPyMySQL==0.9.3\nPyJWT==1.7.1\npytz==2019.3\nrequests==2.22.0\nsix==1.14.0\nsqlparse==0.3.0\nuritemplate==3.0.1\nurllib3==1.25.8\n"
  },
  {
    "path": "backend/init.sh",
    "content": "#!/bin/bash\n\napps=(systems tools notices workflows tickets)\nrm -rf core.db\nfor app in ${apps[@]};do\n  rm -rf $app/migrations\ndone\n\nfor app in ${apps[@]};do\n  echo $app\n  python manage.py makemigrations $app\ndone\n\npython manage.py migrate\npython manage.py init_sys\npython manage.py init_wf\npython manage.py init_ticket\npython manage.py init_leave\npython manage.py runserver"
  },
  {
    "path": "backend/manage.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nimport os\nimport sys\n\nif __name__ == '__main__':\n    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')\n\n    # 排错 “The maximum column size is 767 bytes”\n    from django.db.backends.mysql.schema import DatabaseSchemaEditor\n    DatabaseSchemaEditor.sql_create_table += \" ROW_FORMAT=DYNAMIC\"\n\n    try:\n        from django.core.management import execute_from_command_line\n    except ImportError as exc:\n        raise ImportError(\n            \"Couldn't import Django. Are you sure it's installed and \"\n            \"available on your PYTHONPATH environment variable? Did you \"\n            \"forget to activate a virtual environment?\"\n        ) from exc\n    execute_from_command_line(sys.argv)\n"
  },
  {
    "path": "backend/notices/__init__.py",
    "content": ""
  },
  {
    "path": "backend/notices/management/__init__.py",
    "content": ""
  },
  {
    "path": "backend/notices/management/commands/__init__.py",
    "content": ""
  },
  {
    "path": "backend/notices/management/commands/init_notice.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom django.core.management.base import BaseCommand, CommandError\nfrom systems.models import *\nfrom systems.menus import init_menu\n\n\nclass Command(BaseCommand):\n    help = '初始化工作流'\n\n    def handle(self, *args, **options):\n        topmenu = Menu.objects.get(name='top', code='top')\n        self.stdout.write(self.style.SUCCESS('############ 初始化通知菜单 ###########'))\n        noticemenu = Menu.objects.create(name='通知管理', code='notice', curl='/notice', icon='notice', sequence=5, type=1,\n                                         parent_id=topmenu.id)\n        menumodel = Menu.objects.create(name='mail通知', code='mail', curl='/mail', icon='mail', sequence=10, type=2,\n                                        parent_id=noticemenu.id)\n        init_menu(menumodel)\n        menumodel = Menu.objects.create(name='telegram通知', code='telegram', curl='/telegram', icon='telegram',\n                                        sequence=20, type=2, parent_id=noticemenu.id)\n        init_menu(menumodel)\n        self.stdout.write(self.style.SUCCESS('初始化完成'))\n"
  },
  {
    "path": "backend/notices/migrations/0001_initial.py",
    "content": "# Generated by Django 3.0.3 on 2021-06-27 00:05\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='MailBot',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('type', models.CharField(choices=[('mail', 'mail'), ('telegram', 'telegram')], default=0, max_length=10, verbose_name='通知类型')),\n                ('name', models.CharField(max_length=112, unique=True, verbose_name='名称')),\n                ('host', models.CharField(max_length=112, verbose_name='主机')),\n                ('user', models.CharField(max_length=112, verbose_name='账号')),\n                ('password', models.CharField(max_length=112, verbose_name='密码')),\n                ('to', models.CharField(max_length=112, verbose_name='接收者')),\n            ],\n            options={\n                'verbose_name': '邮件机器人',\n                'verbose_name_plural': '邮件机器人',\n            },\n        ),\n        migrations.CreateModel(\n            name='TelegramBot',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('type', models.CharField(choices=[('mail', 'mail'), ('telegram', 'telegram')], default=0, max_length=10, verbose_name='通知类型')),\n                ('name', models.CharField(max_length=112, unique=True, verbose_name='名称')),\n                ('uid', models.CharField(max_length=112, verbose_name='账号id')),\n                ('token', models.CharField(max_length=112, verbose_name='token')),\n                ('chat_id', models.CharField(max_length=112, verbose_name='chat_id')),\n            ],\n            options={\n                'verbose_name': 'tg机器人',\n                'verbose_name_plural': 'tg机器人',\n            },\n        ),\n    ]\n"
  },
  {
    "path": "backend/notices/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "backend/notices/models.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom django.db import models\nfrom common.models import BaseModel\n\nnotice_type = {\n    'mail': 'mail',\n    'telegram': 'telegram',\n}\n\n\nclass MailBot(BaseModel):\n    type = models.CharField(max_length=10, choices=tuple(notice_type.items()), default=0, verbose_name='通知类型')\n    name = models.CharField(max_length=112, unique=True, verbose_name='名称')\n    host = models.CharField(max_length=112, verbose_name='主机')\n    user = models.CharField(max_length=112, verbose_name='账号')\n    password = models.CharField(max_length=112, verbose_name='密码')\n    to = models.CharField(max_length=112, verbose_name='接收者')\n\n    def __str__(self):\n        return self.name\n\n    class Meta:\n        verbose_name = \"邮件机器人\"\n        verbose_name_plural = verbose_name\n\n\nclass TelegramBot(BaseModel):\n    type = models.CharField(max_length=10, choices=tuple(notice_type.items()), default=0, verbose_name='通知类型')\n    name = models.CharField(max_length=112, unique=True, verbose_name='名称')\n    uid = models.CharField(max_length=112, verbose_name='账号id')\n    token = models.CharField(max_length=112, verbose_name='token')\n    chat_id = models.CharField(max_length=112, verbose_name='chat_id')\n\n    def __str__(self):\n        return self.name\n\n    class Meta:\n        verbose_name = \"tg机器人\"\n        verbose_name_plural = verbose_name\n"
  },
  {
    "path": "backend/notices/serializers.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom notices.models import *\nfrom rest_framework import serializers\n\n\nclass MailBotSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = MailBot\n        fields = '__all__'\n\n\nclass TelegramBotSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = TelegramBot\n        fields = '__all__'\n"
  },
  {
    "path": "backend/notices/urls.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\n\nfrom django.conf.urls import url\nfrom rest_framework import routers\nfrom notices.views import NoticeViewSet, MailBotViewSet, TelegramBotViewSet\n\nrouter = routers.DefaultRouter()\n\nrouter.register(r'notice', NoticeViewSet)\nrouter.register(r'mail', MailBotViewSet)\nrouter.register(r'telegram', TelegramBotViewSet)\n\nurlpatterns = [\n]\n\nurlpatterns += router.urls\n"
  },
  {
    "path": "backend/notices/views.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom notices.serializers import *\nfrom common.views import ModelViewSet, JsonResponse\nfrom common import status\nfrom collections import OrderedDict\nfrom rest_framework.decorators import action\n\n\nclass NoticeViewSet(ModelViewSet):\n    queryset = MailBot.objects.all()\n    serializer_class = MailBotSerializer\n\n    # send\n    @action(methods=['post'], url_path='send', detail=False)\n    def send(self, request, *args, **kwargs):\n        self.watch_audit_log(request)\n        data = {'code': 20000, 'msg': 'null'}\n        type = request.GET['type']\n        if type == 'mail':\n            import smtplib\n            from email.mime.text import MIMEText\n\n            bot_name = request.GET['bot_name']\n            bot_obj = MailBot.objects.get(name=bot_name)\n            mail_user = '{}@{}'.format(bot_obj.user, bot_obj.host)\n\n            tos = bot_obj.to\n            content = request.data.get('content', 'Hello Pornhub')\n            message = MIMEText(content, 'plain', 'utf-8')\n            message['Subject'] = \"{}\".format(request.form['subject'])\n            message['From'] = mail_user\n            message['To'] = tos[0:]\n\n            try:\n                smtpObj = smtplib.SMTP_SSL(bot_obj.host)\n                smtpObj.login(mail_user, bot_obj.pasword)\n                smtpObj.sendmail(mail_user, tos, message.as_string())\n                smtpObj.quit()\n            except smtplib.SMTPException as e:\n                print(e)\n                data['msg'] = 'error'\n        elif type == 'telegram':\n            import telegram\n            bot_name = request.GET['bot_name']\n            content = request.data.get('content', 'Hello Pornhub')\n            bot_obj = TelegramBot.objects.get(name=bot_name)\n            token = '{}:{}'.format(bot_obj.uid, bot_obj.token)\n            bot = telegram.Bot(token=token)\n            data['msg'] = bot.send_message(chat_id=bot_obj.chat_id, text=content)\n        else:\n            pass\n\n        return JsonResponse(OrderedDict([\n            ('results', data)\n        ], code=status.HTTP_200_OK))\n\n\nclass MailBotViewSet(ModelViewSet):\n    queryset = MailBot.objects.all()\n    serializer_class = MailBotSerializer\n    search_fields = ['name']\n    filter_fields = ['type', 'id', 'name']\n    ordering_fields = ['name']\n\n\nclass TelegramBotViewSet(ModelViewSet):\n    queryset = TelegramBot.objects.all()\n    serializer_class = TelegramBotSerializer\n    search_fields = ['name']\n    filter_fields = ['type', 'id', 'name']\n    ordering_fields = ['name']\n"
  },
  {
    "path": "backend/oms.ini",
    "content": "[uwsgi]\nproject = core\nbase = /data/app/one/backend\n\nchdir = %(base)\nmodule = %(project).wsgi:application\n\nmaster = true\nprocesses = 5\nenable-threads = true\n\nsocket = %(base)/%(project).sock\nchmod-socket = 666\nvacuum = true\nlogto = /data/logs/django/one.log\n"
  },
  {
    "path": "backend/oms.service",
    "content": "[Unit]\nDescription=uWSGI instance to serve one-oms\n\n[Service]\nType=simple\nUser=root\nGroup=root\nWorkingDirectory=/data/app/one/backend\nExecStart=/root/.pyenv/versions/boce/bin/uwsgi --ini oms.ini --touch-reload=/etc/nginx/uwsgi_params\nRestart=on-failure\nKillSignal=SIGQUIT\nType=notify\nNotifyAccess=all\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "backend/requirements.txt",
    "content": "asgiref==3.2.3\ncertifi==2019.11.28\nchardet==3.0.4\ncoreapi==2.3.3\ncoreschema==0.0.4\nDjango==3.0.3\ndjango-cors-headers==3.2.1\ndjango-filter==2.2.0\ndjango-rest-auth==0.9.5\ndjangorestframework==3.11.0\ndjangorestframework-jwt==1.11.0\nidna==2.8\nIPy==1.0\nitypes==1.1.0\nJinja2==2.10.3\nMarkupSafe==1.1.1\npython-telegram-bot==12.6.1\nmysqlclient==1.4.6\nPyJWT==1.7.1\npytz==2019.3\nrequests==2.22.0\nsix==1.14.0\nsqlparse==0.3.0\nuritemplate==3.0.1\nurllib3==1.25.8\n"
  },
  {
    "path": "backend/systems/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\n"
  },
  {
    "path": "backend/systems/admin.py",
    "content": "from django.contrib import admin\nfrom systems.models import *\n\nadmin.site.register(Menu)\nadmin.site.register(Role)\nadmin.site.register(Group)\nadmin.site.register(User)\n"
  },
  {
    "path": "backend/systems/management/__init__.py",
    "content": ""
  },
  {
    "path": "backend/systems/management/commands/__init__.py",
    "content": ""
  },
  {
    "path": "backend/systems/management/commands/init_sys.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom django.core.management.base import BaseCommand, CommandError\nfrom systems.models import *\nfrom systems.menus import init_menu\nfrom django.contrib.auth.hashers import make_password\n\n\nclass Command(BaseCommand):\n    help = '初始化 菜单 角色 用户 用户组'\n\n    def handle(self, *args, **options):\n        try:\n            self.stdout.write(self.style.SUCCESS('############ 初始化角色 ###########'))\n            role = Role.objects.create(name='top', code='top', sequence=0, parent=None)\n        except:\n            raise CommandError('初始化角色失败')\n\n        try:\n            self.stdout.write(self.style.SUCCESS('############ 初始化用户组 ###########'))\n            group = Group.objects.create(name='top', code='top', sequence=0, parent=None)\n            group.roles.add(role)\n        except:\n            raise CommandError('初始化用户组失败')\n\n        try:\n            self.stdout.write(self.style.SUCCESS('############ 初始化用户 ###########'))\n            user = User.objects.create(username='admin', password=make_password(\"123456\"), group=group, is_admin=True)\n            user.roles.add(role)\n        except:\n            raise CommandError('初始化用户失败')\n\n        menus = Menu.objects.all()\n        if len(menus) == 0:\n            topmenu = Menu.objects.create(name='top', code='top', curl='/top', icon='top', sequence=0, type=1, parent=None)\n            self.stdout.write(self.style.SUCCESS('############ 初始化系统菜单 ###########'))\n            sysmenu = Menu.objects.create(name='系统管理', code='sys', curl='/sys', icon='sys', sequence=1, type=1, parent=topmenu)\n            menumodel = Menu.objects.create(name='角色管理', code='role', curl='/role', icon='role', sequence=30, type=2, parent=sysmenu)\n            init_menu(menumodel)\n            menumodel = Menu.objects.create(name='分组管理', code='group', curl='/group', icon='group', sequence=10, type=2, parent=sysmenu)\n            init_menu(menumodel)\n            menumodel = Menu.objects.create(name='用户管理', code='user', curl='/user', icon='user', sequence=20, type=2, parent=sysmenu)\n            init_menu(menumodel)\n            menumodel = Menu.objects.create(name='菜单管理', code='menu', curl='/menu', icon='menu', sequence=40, type=2, parent=sysmenu)\n            init_menu(menumodel)\n            menumodel = Menu.objects.create(name='图标管理', code='icon', curl='/icon', icon='icon', sequence=50, type=2, parent=sysmenu)\n            init_menu(menumodel)\n\n            self.stdout.write(self.style.SUCCESS('############ 初始化工具菜单 ###########'))\n            toolmenu = Menu.objects.create(name='工具管理', code='tool', curl='/tool', icon='tool', sequence=2, type=1, parent=topmenu)\n            menumodel = Menu.objects.create(name='审计日志', code='audit', curl='/audit', icon='audit', sequence=10, type=2, parent=toolmenu)\n            init_menu(menumodel)\n            menumodel = Menu.objects.create(name='测试页面', code='test', curl='/test', icon='list', sequence=20, type=2, parent=toolmenu)\n            init_menu(menumodel)\n\n            self.stdout.write(self.style.SUCCESS('############ 初始化通知菜单 ###########'))\n            noticemenu = Menu.objects.create(name='通知管理', code='notice', curl='/notice', icon='notice', sequence=4, type=1, parent=topmenu)\n            menumodel = Menu.objects.create(name='邮箱通知', code='mail', curl='/mail', icon='mail', sequence=10, type=2, parent=noticemenu)\n            init_menu(menumodel)\n            menumodel = Menu.objects.create(name='telegram通知', code='telegram', curl='/telegram', icon='telegram', sequence=20, type=2, parent=noticemenu)\n            init_menu(menumodel)\n\n        self.stdout.write(self.style.SUCCESS('初始化完成'))\n"
  },
  {
    "path": "backend/systems/menus.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom systems.models import *\nfrom itertools import chain\n\n\n# 获取管理员权限下所有菜单\ndef get_menus_by_user(user):\n    user_obj = User.objects.get(username=user)\n    if user_obj.is_admin:\n        menus = Menu.objects.all()\n        all_roles = Role.objects.all()\n    else:\n        user_roles = user_obj.roles.all()\n        group_roles = user_obj.group.roles.all()\n        all_roles = sorted(chain(user_roles, group_roles), key=lambda t: t.id, reverse=True)\n        print(\"用户拥有角色: %s\" % all_roles)\n\n        menu_list = [item.menus.all() for item in all_roles if item.menus.all()][0]\n\n        menuMap = dict()\n        for item in menu_list:\n            menuMap[item.id] = item\n        for item in menu_list:\n            if item.parent_id in menuMap:\n                continue\n            user_menus = find_menu_daddy(item.parent_id, menuMap)\n        menus = [user_menus[i] for i in sorted(user_menus.keys())]\n    roles = [i.code for i in all_roles]\n    return menus, roles\n\n\ndef find_menu_daddy(menuid, menuMap):\n    obj = Menu.objects.filter(id=menuid).first()\n    if obj:\n        mid = obj.id\n        if mid not in menuMap:\n            menuMap[mid] = obj\n            find_menu_daddy(obj.parent_id, menuMap)\n            return menuMap\n\n\ndef set_menu(menus, parent_id):\n    amenus = [i for i in menus if i.parent_id == parent_id]\n\n    if len(amenus) == 0:\n        return []\n\n    all_menus = []\n    for item in amenus:\n        menu = {\n            'path': item.curl,\n            'component': item.code,\n            'name': item.code,\n            'hidden': item.hidden,\n            'meta': {'title': item.code, 'icon': item.icon, 'no_cache': item.no_cache, 'active_menu': item.active_menu,\n                     'hidden': item.hidden,\n                     },\n            'children': []\n        }\n        if item.type == 3:\n            menu['hidden'] = True\n\n        # 查询是否有子级\n        menu_children = set_menu(menus, item.id)\n        if len(menu_children) > 0:\n            menu['children'] = menu_children\n\n        if item.type == 2:\n            # 添加子级首页，有这一级NoCache才有效\n            menu_index = {\n                'path': 'index',\n                'component': item.code,\n                'name': item.code,\n                'hidden': item.hidden,\n                'meta': {'title': item.code, 'icon': item.icon, 'no_cache': item.no_cache,\n                         'active_menu': item.active_menu, 'hidden': item.hidden,\n                         },\n                'children': []\n            }\n            menu['children'].append(menu_index)\n            menu['meta'] = []\n\n        all_menus.append(menu)\n    return all_menus\n\n\n# 新增菜单后自动添加菜单下的常规操作\ndef init_menu(menu):\n    if menu.type == 2:\n        menu_list = [\n            {\"name\": \"新增\", \"code\": menu.code + \"_add\", \"curl\": menu.curl + \"/add\", 'type': 3, \"operate\": \"add\",\n             \"sequence\": 10},\n            {\"name\": \"删除\", \"code\": menu.code + \"_del\", \"curl\": menu.curl + \"/del\", 'type': 3, \"operate\": \"del\",\n             \"sequence\": 20},\n            {\"name\": \"编辑\", \"code\": menu.code + \"_update\", \"curl\": menu.curl + \"/update\", 'type': 3, \"operate\": \"update\",\n             \"sequence\": 30},\n            {\"name\": \"查看\", \"code\": menu.code + \"_view\", \"curl\": menu.curl + \"/view\", 'type': 3, \"operate\": \"view\",\n             \"sequence\": 40},\n        ]\n\n        menu_models = []\n        for item in menu_list:\n            menu_models.append(Menu(name=item['name'], code=item['code'], curl=item['curl'], type=item['type'],\n                                    operate=item['operate'],\n                                    sequence=item['sequence'], parent_id=menu.id, hidden=True))\n        Menu.objects.bulk_create(menu_models)\n"
  },
  {
    "path": "backend/systems/migrations/0001_initial.py",
    "content": "# Generated by Django 3.0.3 on 2021-06-27 00:05\n\nimport django.contrib.auth.models\nfrom django.db import migrations, models\nimport django.db.models.deletion\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n        ('auth', '0011_update_proxy_permissions'),\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='Menu',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('name', models.CharField(max_length=32, verbose_name='菜单名称')),\n                ('code', models.CharField(max_length=32, verbose_name='菜单代码')),\n                ('curl', models.CharField(max_length=101, verbose_name='菜单URL')),\n                ('icon', models.CharField(blank=True, max_length=32, verbose_name='菜单图标')),\n                ('hidden', models.BooleanField(default=False, verbose_name='菜单是否隐藏')),\n                ('no_cache', models.BooleanField(default=True, verbose_name='菜单是否缓存')),\n                ('active_menu', models.CharField(blank=True, max_length=32, verbose_name='激活菜单')),\n                ('sequence', models.SmallIntegerField(default=0, verbose_name='排序值')),\n                ('type', models.CharField(choices=[(1, '模块'), (2, '菜单'), (3, '操作')], default=2, max_length=1, verbose_name='菜单类型')),\n                ('status', models.BooleanField(default=True, verbose_name='状态')),\n                ('operate', models.CharField(choices=[('none', '无'), ('add', '新增'), ('del', '删除'), ('update', '编辑'), ('view', '查看')], default='none', max_length=11, verbose_name='操作类型')),\n                ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='systems.Menu', verbose_name='父级菜单')),\n            ],\n            options={\n                'verbose_name': '角色',\n                'verbose_name_plural': '角色',\n                'ordering': ['id'],\n            },\n        ),\n        migrations.CreateModel(\n            name='Role',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('name', models.CharField(max_length=32, unique=True, verbose_name='名称')),\n                ('code', models.CharField(max_length=32, unique=True, verbose_name='代码')),\n                ('sequence', models.SmallIntegerField(default=0, verbose_name='排序值')),\n                ('menus', models.ManyToManyField(blank=True, to='systems.Menu', verbose_name='菜单')),\n                ('model_perms', models.ManyToManyField(blank=True, to='auth.Permission', verbose_name='model权限')),\n                ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='systems.Role', verbose_name='父级角色')),\n            ],\n            options={\n                'verbose_name': '角色',\n                'verbose_name_plural': '角色',\n            },\n        ),\n        migrations.CreateModel(\n            name='Group',\n            fields=[\n                ('group_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='auth.Group')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('code', models.CharField(max_length=32, unique=True, verbose_name='代码')),\n                ('sequence', models.SmallIntegerField(default=0, verbose_name='排序值')),\n                ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='systems.Group', verbose_name='父级角色')),\n                ('roles', models.ManyToManyField(blank=True, to='systems.Role', verbose_name='roles')),\n            ],\n            options={\n                'verbose_name': '分组',\n                'verbose_name_plural': '分组',\n            },\n            bases=('auth.group', models.Model),\n            managers=[\n                ('objects', django.contrib.auth.models.GroupManager()),\n            ],\n        ),\n        migrations.CreateModel(\n            name='User',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('password', models.CharField(max_length=128, verbose_name='password')),\n                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('username', models.CharField(db_index=True, max_length=32, unique=True)),\n                ('realname', models.CharField(blank=True, default='图书馆管理员', max_length=32, verbose_name='真实名字')),\n                ('email', models.EmailField(blank=True, default='itimor@126.com', max_length=254, verbose_name='邮箱')),\n                ('avatar', models.CharField(default='http://m.imeitou.com/uploads/allimg/2017110610/b3c433vwhsk.jpg', max_length=255)),\n                ('status', models.BooleanField(default=True)),\n                ('is_admin', models.BooleanField(default=False)),\n                ('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_set', related_query_name='user', to='systems.Group', verbose_name='group')),\n                ('model_perms', models.ManyToManyField(blank=True, related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),\n                ('roles', models.ManyToManyField(blank=True, related_name='user_set', related_query_name='user', to='systems.Role', verbose_name='roles')),\n            ],\n            options={\n                'verbose_name': '用户',\n                'verbose_name_plural': '用户',\n            },\n        ),\n    ]\n"
  },
  {
    "path": "backend/systems/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "backend/systems/models.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom django.db import models\nfrom django.contrib.auth.models import Permission, Group, GroupManager\nfrom django.contrib.auth.models import BaseUserManager, AbstractBaseUser\nfrom systems.models import *\nfrom common.models import BaseModel\n\nmenu_type = {\n    1: '模块',\n    2: '菜单',\n    3: '操作',\n}\n\noperate_type = {\n    'none': '无',\n    'add': '新增',\n    'del': '删除',\n    'update': '编辑',\n    'view': '查看',\n}\n\n\nclass Menu(BaseModel):\n    parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.SET_NULL, verbose_name='父级菜单')\n    name = models.CharField(max_length=32, verbose_name='菜单名称')\n    code = models.CharField(max_length=32, verbose_name='菜单代码')\n    curl = models.CharField(max_length=101, verbose_name='菜单URL')\n    icon = models.CharField(max_length=32, blank=True, verbose_name='菜单图标')\n    hidden = models.BooleanField(default=False, verbose_name='菜单是否隐藏')\n    no_cache = models.BooleanField(default=True, verbose_name='菜单是否缓存')\n    active_menu = models.CharField(max_length=32, blank=True, verbose_name='激活菜单')\n    sequence = models.SmallIntegerField(default=0, verbose_name='排序值')\n    type = models.CharField(max_length=1, choices=tuple(menu_type.items()), default=2, verbose_name='菜单类型')\n    status = models.BooleanField(default=True, verbose_name='状态')\n    operate = models.CharField(max_length=11, choices=tuple(operate_type.items()), default='none', verbose_name='操作类型')\n\n    def __str__(self):\n        return \"{parent}{name}\".format(name=self.name, parent=\"%s-->\" % self.parent.name if self.parent else '')\n\n    class Meta:\n        verbose_name = '菜单'\n        verbose_name_plural = verbose_name\n        ordering = ['id']\n\n\nclass Role(BaseModel):\n    parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.SET_NULL, verbose_name='父级角色')\n    name = models.CharField(max_length=32, unique=True, verbose_name='名称')\n    code = models.CharField(max_length=32, unique=True, verbose_name='代码')\n    sequence = models.SmallIntegerField(default=0, verbose_name='排序值')\n    menus = models.ManyToManyField(Menu, blank=True, verbose_name='菜单')\n    model_perms = models.ManyToManyField(Permission, blank=True, verbose_name='model权限')\n\n    def __str__(self):\n        return \"{parent}{name}\".format(name=self.name, parent=\"%s-->\" % self.parent.name if self.parent else '')\n\n    class Meta:\n        verbose_name = '角色'\n        verbose_name_plural = verbose_name\n        ordering = ['id']\n\n\nclass Group(BaseModel, Group):\n    parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.SET_NULL, verbose_name='父级角色')\n    code = models.CharField(max_length=32, unique=True, verbose_name='代码')\n    sequence = models.SmallIntegerField(default=0, verbose_name='排序值')\n    roles = models.ManyToManyField(Role, verbose_name='roles', blank=True, )\n\n    def __str__(self):\n        return \"{parent}{name}\".format(name=self.name, parent=\"%s-->\" % self.parent.name if self.parent else '')\n\n    class Meta:\n        verbose_name = '分组'\n        verbose_name_plural = verbose_name\n        ordering = ['id']\n\n    objects = GroupManager()  # 创建用户\n\n\nclass PermissionsMixin(models.Model):\n    group = models.ForeignKey(\n        Group,\n        verbose_name='group',\n        null=True,\n        blank=True,\n        on_delete=models.SET_NULL,\n        related_name=\"user_set\",\n        related_query_name=\"user\",\n    )\n    roles = models.ManyToManyField(\n        Role,\n        verbose_name='roles',\n        blank=True,\n        related_name=\"user_set\",\n        related_query_name=\"user\",\n    )\n    model_perms = models.ManyToManyField(\n        Permission,\n        verbose_name='user permissions',\n        blank=True,\n        related_name=\"user_set\",\n        related_query_name=\"user\",\n    )\n\n    class Meta:\n        abstract = True\n        ordering = ['id']\n\n\nclass UserManager(BaseUserManager):\n    def create_user(self, username, password=None):\n        \"\"\"\n        username 是唯一标识，没有会报错\n        \"\"\"\n\n        if not username:\n            raise ValueError('Users must have an username')\n\n        user = self.model(\n            username=username,\n        )\n        user.set_password(password)  # 检测密码合理性\n        user.save(using=self._db)  # 保存密码\n        return user\n\n    def create_superuser(self, username, password):\n        user = self.create_user(username=username,\n                                password=password,\n                                )\n        user.is_admin = True  # 比创建用户多的一个字段\n        user.save(using=self._db)\n        return user\n\n\nclass User(BaseModel, PermissionsMixin, AbstractBaseUser):\n    username = models.CharField(max_length=32, unique=True, db_index=True)\n    realname = models.CharField(max_length=32, default=\"图书馆管理员\", blank=True, verbose_name='真实名字')\n    email = models.EmailField(blank=True, default=\"itimor@126.com\", verbose_name='邮箱')\n    avatar = models.CharField(max_length=255, default='https://pica.zhimg.com/80/v2-e4e933375b971b0941907ff9d7985188_640w.jpg')\n    status = models.BooleanField(default=True)\n    is_admin = models.BooleanField(default=False)\n\n    USERNAME_FIELD = 'username'  # 必须有一个唯一标识--USERNAME_FIELD\n\n    @property\n    def is_staff(self):\n        return self.is_admin\n\n    def has_perm(self, perm, obj=None):\n        return self.is_admin\n\n    def has_module_perms(self, app_label):\n        return self.is_admin\n\n    def __str__(self):\n        return self.username\n\n    class Meta:\n        verbose_name = '用户'\n        verbose_name_plural = verbose_name\n        ordering = ['id']\n\n    objects = UserManager()  # 创建用户\n"
  },
  {
    "path": "backend/systems/permissions.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\n\nfrom rest_framework.permissions import BasePermission\nfrom systems.models import *\nfrom itertools import chain\n\nparse_method_action = {\n    'GET': 'view',\n    'POST': 'add',\n    'PUT': 'change',\n    'PATCH': 'change',\n    'DELETE': 'delete',\n}\n\nignore_path = [\n    '/api/sys/auth/jwt-token-auth/',\n    '/api/sys/auth/getuserinfo/',\n    '/api/sys/auth/getmenubutons/',\n]\n\n\ndef check_permission(request, perm):\n    user = User.objects.get(username=request.user)\n\n    if user.is_admin:\n        return True\n\n    if request.path in ignore_path:\n        return True\n\n    user_roles = user.roles.all()\n    group_roles = user.group.roles.all()\n    all_roles = sorted(chain(user_roles, group_roles), key=lambda t: t.id, reverse=True)\n    perms = Permission.objects.filter(role__in=all_roles)\n    for i in perms:\n        if i.codename == perm:\n            return True\n\n\nclass IsOwnerRoles(BasePermission):\n\n    def has_permission(self, request, view):\n        app = view.get_view_name().split()\n        object = ''.join(app[:-1]).lower()\n        perm = 'view_{}'.format(object)\n        return check_permission(request, perm)\n\n    def has_object_permission(self, request, view, obj):\n        app_label = obj._meta.app_label\n        model = obj._meta.object_name.lower()\n        perm = '{}_{}'.format(parse_method_action[request.method], model)\n        return check_permission(request, perm)\n"
  },
  {
    "path": "backend/systems/serializers.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom systems.models import *\nfrom systems.menus import init_menu\nfrom rest_framework import serializers\n\n\nclass UserReadSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = User\n        fields = '__all__'\n        depth = 1\n\n\nclass UserSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = User\n        fields = '__all__'\n        extra_kwargs = {'password': {'write_only': True, 'required': False}}\n\n    def create(self, validated_data):\n        roles = validated_data.pop('roles')\n        obj = User.objects.create(**validated_data)\n        if len(roles) > 0:\n            obj.roles.set(roles)\n        else:\n            role = Role.objects.get(id=1)\n            obj.roles.add(role)\n        try:\n            obj.set_password(validated_data['password'])\n        except:\n            pass\n        obj.save()\n        return obj\n\n    def update(self, instance, validated_data):\n        roles = validated_data.pop('roles')\n        instance.username = validated_data.get('username', instance.username)\n        instance.realname = validated_data.get('realname', instance.realname)\n        instance.group = validated_data.get('group', instance.group)\n        instance.email = validated_data.get('email', instance.email)\n        instance.avatar = validated_data.get('avatar', instance.avatar)\n        instance.status = validated_data.get('status', instance.status)\n        instance.memo = validated_data.get('memo', instance.memo)\n        try:\n            instance.set_password(validated_data['password'])\n        except:\n            pass\n        instance.roles.set(roles)\n        instance.save()\n        return instance\n\n\nclass RoleSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Role\n        fields = '__all__'\n\n\nclass GroupSerializer(serializers.ModelSerializer):\n    user_set = UserSerializer(many=True, read_only=True)\n\n    class Meta:\n        model = Group\n        fields = '__all__'\n\n\nclass PermissionSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Permission\n        fields = '__all__'\n\n\nclass MenuSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Menu\n        fields = '__all__'\n\n    def create(self, validated_data):\n        obj = Menu.objects.create(**validated_data)\n        if obj.type == 2:\n            init_menu(obj)\n        return obj\n"
  },
  {
    "path": "backend/systems/urls.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\n\nfrom django.conf.urls import url, include\nfrom rest_framework import routers\nfrom rest_auth.views import PasswordChangeView\nfrom systems.views import UserViewSet, GroupViewSet, RoleViewSet, PermissionViewSet, MenuViewSet, AuthViewSet, \\\n    ObtainJSONWebToken\n\nrouter = routers.DefaultRouter()\n\nrouter.register(r'user', UserViewSet)\nrouter.register(r'group', GroupViewSet)\nrouter.register(r'role', RoleViewSet)\nrouter.register(r'perm', PermissionViewSet)\nrouter.register(r'menu', MenuViewSet)\nrouter.register(r'auth', AuthViewSet)\n\nurlpatterns = [\n    url(r'^auth/changepwd/', PasswordChangeView.as_view(), name='changepwd'),\n    # token认证\n    url(r'^auth/jwt-token-auth/', ObtainJSONWebToken.as_view(), name='rest_framework_token'),\n    url(r'^auth/api-token-auth/', include('rest_framework.urls', namespace='rest_framework')),\n]\n\nurlpatterns += router.urls\n"
  },
  {
    "path": "backend/systems/views.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom systems.serializers import *\nfrom common.views import ModelViewSet, FKModelViewSet, JsonResponse, BulkModelMixin\nfrom rest_framework.decorators import action\nfrom systems.menus import get_menus_by_user, set_menu\nfrom common import status\nfrom collections import OrderedDict\nfrom rest_framework_jwt.serializers import JSONWebTokenSerializer\nfrom rest_framework_jwt.views import JSONWebTokenAPIView, jwt_response_payload_handler\nfrom rest_framework_jwt.settings import api_settings\nfrom datetime import datetime\n\n\nclass UserViewSet(BulkModelMixin):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    search_fields = ['username']\n    filter_fields = ['username', 'group', 'roles']\n    ordering_fields = ['username', 'status']\n\n\nclass GroupViewSet(BulkModelMixin):\n    queryset = Group.objects.all()\n    serializer_class = GroupSerializer\n    search_fields = ['name']\n    filter_fields = ['name']\n    ordering_fields = ['parent_id', 'sequence']\n\n\nclass RoleViewSet(BulkModelMixin):\n    queryset = Role.objects.all()\n    serializer_class = RoleSerializer\n    search_fields = ['name']\n    filter_fields = ['name']\n    ordering_fields = ['parent_id', 'sequence']\n\n\nclass PermissionViewSet(ModelViewSet):\n    queryset = Permission.objects.all()\n    serializer_class = PermissionSerializer\n    search_fields = ['name']\n    filter_fields = ['name']\n    ordering_fields = ['name']\n\n\nclass MenuViewSet(BulkModelMixin):\n    queryset = Menu.objects.all()\n    serializer_class = MenuSerializer\n    search_fields = ['name']\n    filter_fields = ['name']\n    ordering_fields = ['parent_id', 'sequence']\n\n\nclass AuthViewSet(ModelViewSet):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n\n    @action(methods=['get'], url_path='getuserinfo', detail=False)\n    def getuserinfo(self, request):\n        user = request.user\n        user_obj = User.objects.get(username=user)\n\n        data, roles = get_menus_by_user(user)\n\n        if len(data) > 0:\n            topmenuid = data[0].parent_id\n            if not topmenuid:\n                topmenuid = data[0].id\n\n        menus = set_menu(data, topmenuid)\n\n        ip = request.META.get(\"HTTP_X_FORWARDED_FOR\", \"\")\n        if not ip:\n            ip = request.META.get('REMOTE_ADDR', \"\")\n\n        data = {'menus': menus, 'username': user_obj.username, 'avatar': user_obj.avatar, 'memo': user_obj.memo,\n                'ip': ip, 'user_id': user_obj.id, 'roles': roles}\n        return JsonResponse(OrderedDict([\n            ('results', data)\n        ], code=status.HTTP_200_OK))\n\n    @action(methods=['get'], url_path='getmenubutons', detail=False)\n    def getmenubutons(self, request):\n        user = request.user\n        user_obj = User.objects.get(username=user)\n        buttons = []\n\n        if user_obj.is_admin:\n            buttons = ['add', 'del', 'update', 'view']\n        else:\n            menucode = request.GET['menucode']\n\n            match_menu = Menu.objects.get(code=menucode)\n\n            data, roles = get_menus_by_user(user)\n            for item in data:\n                if item.parent_id == match_menu.id:\n                    buttons.append(item.operate)\n        data = buttons\n        return JsonResponse(OrderedDict([\n            ('results', data)\n        ], code=status.HTTP_200_OK))\n\n\nclass ObtainJSONWebToken(JSONWebTokenAPIView):\n    serializer_class = JSONWebTokenSerializer\n\n    def post(self, request, *args, **kwargs):\n        serializer = self.get_serializer(data=request.data)\n\n        if serializer.is_valid():\n            user = serializer.object.get('user') or request.user\n            token = serializer.object.get('token')\n            response_data = jwt_response_payload_handler(token, user, request)\n            response = JsonResponse(OrderedDict([\n                ('results', response_data)\n            ], code=status.HTTP_200_OK))\n            if api_settings.JWT_AUTH_COOKIE:\n                expiration = (datetime.utcnow() +\n                              api_settings.JWT_EXPIRATION_DELTA)\n                response.set_cookie(api_settings.JWT_AUTH_COOKIE,\n                                    token,\n                                    expires=expiration,\n                                    httponly=True)\n            return response\n\n        return JsonResponse(OrderedDict([\n            ('results', serializer.errors)\n        ], code=status.HTTP_500_INTERNAL_SERVER_ERROR))\n"
  },
  {
    "path": "backend/tickets/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\n"
  },
  {
    "path": "backend/tickets/filters.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom tickets.models import *\nfrom django_filters import rest_framework as filters\n\n\nclass TicketFilter(filters.FilterSet):\n    class Meta:\n        model = Ticket\n\n        fields = {\n            'id': ['exact'],\n            'name': ['exact'],\n            'participant': ['exact'],\n            'create_user__username': ['exact'],\n            \"transition__attribute_type\": ['exact', \"lt\"],\n        }\n"
  },
  {
    "path": "backend/tickets/management/__init__.py",
    "content": ""
  },
  {
    "path": "backend/tickets/management/commands/__init__.py",
    "content": ""
  },
  {
    "path": "backend/tickets/management/commands/init_ticket.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom django.core.management.base import BaseCommand, CommandError\nfrom systems.models import *\nfrom systems.menus import init_menu\n\n\nclass Command(BaseCommand):\n    help = '初始化工作流'\n\n    def handle(self, *args, **options):\n        topmenu = Menu.objects.get(name='top', code='top')\n        self.stdout.write(self.style.SUCCESS('############ 初始化工单菜单 ###########'))\n        ticketmenu = Menu.objects.create(name='工单系统', code='ticket', curl='/ticket', icon='ticket', sequence=4, type=1,\n                                      parent_id=topmenu.id)\n        menumodel = Menu.objects.create(name='新建工单', code='new_ticket', curl='/new_ticket', icon='new_ticket', sequence=10, type=2,\n                                        parent_id=ticketmenu.id)\n        init_menu(menumodel)\n        menumodel = Menu.objects.create(name='编辑工单', code='u_ticket', curl='/u_ticket/:id', icon='u_ticket', sequence=10, type=2,\n                                        hidden=True, active_menu='/new_ticket', parent_id=ticketmenu.id)\n        init_menu(menumodel)\n        menumodel = Menu.objects.create(name='审批工单', code='s_ticket', curl='/s_ticket/:id', icon='s_ticket', sequence=10, type=2,\n                                        hidden=True, active_menu='/todo_ticket', parent_id=ticketmenu.id)\n        init_menu(menumodel)\n        menumodel = Menu.objects.create(name='我的工单', code='my_ticket', curl='/my_ticket', icon='my_ticket', sequence=30, type=2,\n                                        no_cache=True, parent_id=ticketmenu.id)\n        init_menu(menumodel)\n        menumodel = Menu.objects.create(name='我的待办', code='todo_ticket', curl='/todo_ticket', icon='todo_ticket', sequence=40, type=2,\n                                        no_cache=True, parent_id=ticketmenu.id)\n        init_menu(menumodel)\n        menumodel = Menu.objects.create(name='所有工单', code='all_ticket', curl='/all_ticket', icon='all_ticket', sequence=90, type=2,\n                                        no_cache=True, parent_id=ticketmenu.id)\n        init_menu(menumodel)\n        self.stdout.write(self.style.SUCCESS('初始化完成'))\n"
  },
  {
    "path": "backend/tickets/migrations/0001_initial.py",
    "content": "# Generated by Django 3.0.3 on 2021-06-27 00:05\n\nfrom django.conf import settings\nfrom django.db import migrations, models\nimport django.db.models.deletion\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n        ('workflows', '0001_initial'),\n        migrations.swappable_dependency(settings.AUTH_USER_MODEL),\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='Ticket',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('name', models.CharField(blank=True, default='', max_length=112, verbose_name='标题')),\n                ('sn', models.CharField(blank=True, help_text='工单的流水号', max_length=25, verbose_name='流水号')),\n                ('participant', models.CharField(blank=True, default='', max_length=50, verbose_name='当前处理人')),\n                ('customfield', models.TextField(default=[], verbose_name='所有表单数据')),\n                ('relation', models.TextField(blank=True, default='', help_text='创建人、处理人，用于查询', verbose_name='工单关联人')),\n                ('create_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='创建者')),\n                ('state', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflows.State', verbose_name='当前状态')),\n                ('transition', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='workflows.Transition', verbose_name='进行状态')),\n                ('workflow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflows.Workflow', verbose_name='工作流')),\n            ],\n            options={\n                'verbose_name': '工单记录',\n                'verbose_name_plural': '工单记录',\n            },\n        ),\n        migrations.CreateModel(\n            name='TicketUser',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('username', models.CharField(max_length=100, verbose_name='关系人')),\n                ('in_process', models.BooleanField(default=False, verbose_name='待处理中')),\n                ('worked', models.BooleanField(default=False, verbose_name='处理过')),\n                ('ticket', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='tickets.Ticket')),\n            ],\n            options={\n                'verbose_name': '工单关系人',\n                'verbose_name_plural': '工单关系人',\n            },\n        ),\n        migrations.CreateModel(\n            name='TicketFlowLog',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('suggestion', models.CharField(blank=True, max_length=140, verbose_name='审批意见')),\n                ('participant', models.CharField(blank=True, default='', max_length=50, verbose_name='处理人')),\n                ('intervene_type', models.CharField(choices=[(0, '转交操作'), (1, '接单操作'), (2, '评论操作'), (3, '删除操作'), (4, '强制关闭操作'), (5, '强制修改状态操作'), (6, '撤回')], default=0, max_length=1, verbose_name='干预类型')),\n                ('state', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflows.State', verbose_name='当前状态')),\n                ('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tickets.Ticket', verbose_name='工单')),\n                ('transition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflows.Transition', verbose_name='流转')),\n            ],\n            options={\n                'verbose_name': '工单流转日志',\n                'verbose_name_plural': '工单流转日志',\n            },\n        ),\n        migrations.CreateModel(\n            name='TicketCustomField',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('field_value', models.TextField(blank=True, default='', verbose_name='字段值')),\n                ('customfield', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflows.CustomField', verbose_name='字段')),\n                ('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tickets.Ticket', verbose_name='工单')),\n            ],\n            options={\n                'verbose_name': '工单自定义字段值',\n                'verbose_name_plural': '工单自定义字段值',\n            },\n        ),\n    ]\n"
  },
  {
    "path": "backend/tickets/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "backend/tickets/models.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom django.db import models\nfrom common.models import BaseModel\nfrom workflows.models import *\nfrom systems.models import User\n\n\nclass Ticket(BaseModel):\n    \"\"\"\n    工单记录\n    \"\"\"\n    name = models.CharField(u'标题', max_length=112, blank=True, default='')\n    sn = models.CharField(u'流水号', max_length=25, blank=True, help_text=\"工单的流水号\")\n    create_user = models.ForeignKey(User, verbose_name='创建者', blank=True, null=True, on_delete=models.SET_NULL)\n    participant = models.CharField('当前处理人', max_length=50, default='', blank=True)\n    workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='工作流')\n    state = models.ForeignKey(State, on_delete=models.CASCADE, verbose_name='当前状态')\n    transition = models.ForeignKey(Transition, on_delete=models.SET_NULL, blank=True, null=True, verbose_name='进行状态')\n    customfield = models.TextField('所有表单数据', default=[])\n    relation = models.TextField('工单关联人', default='', blank=True, help_text='创建人、处理人，用于查询')\n\n    def __str__(self):\n        return self.name\n\n    class Meta:\n        verbose_name = '工单记录'\n        verbose_name_plural = verbose_name\n\n\nintervene_type = {\n    0: '转交操作',\n    1: '接单操作',\n    2: '评论操作',\n    3: '删除操作',\n    4: '强制关闭操作',\n    5: '强制修改状态操作',\n    6: '撤回',\n}\n\n\nclass TicketFlowLog(BaseModel):\n    \"\"\"\n    工单流转日志\n    \"\"\"\n    ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='工单')\n    suggestion = models.CharField('审批意见', max_length=140, blank=True)\n    transition = models.ForeignKey(Transition, on_delete=models.CASCADE, verbose_name='流转')\n    participant = models.CharField('处理人', max_length=50, default='', blank=True)\n    state = models.ForeignKey(State, on_delete=models.CASCADE, verbose_name='当前状态')\n    intervene_type = models.CharField(max_length=1, choices=tuple(intervene_type.items()), default=0,\n                                      verbose_name='干预类型')\n\n    class Meta:\n        verbose_name = '工单流转日志'\n        verbose_name_plural = verbose_name\n\n\nfield_type = {\n    10: '字符串',\n    15: '整形',\n    20: '浮点型',\n    25: '布尔',\n    30: '日期',\n    35: '时间',\n    40: '日期时间',\n    45: '单选框',\n    50: '多选框',\n    55: '下拉列表',\n    60: '多选下拉列表',\n    65: '文本域',\n    70: '用户名',\n    75: '多选的用户名',\n}\n\n\nclass TicketCustomField(BaseModel):\n    \"\"\"\n    工单自定义字段， 工单自定义字段实际的值。\n    \"\"\"\n    ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='工单')\n    customfield = models.ForeignKey(CustomField, on_delete=models.CASCADE, verbose_name='字段')\n    field_value = models.TextField('字段值', default='', blank=True)\n\n    class Meta:\n        verbose_name = '工单自定义字段值'\n        verbose_name_plural = verbose_name\n\n\nclass TicketUser(BaseModel):\n    \"\"\"\n    工单关系人, 用于加速待办工单及关联工单列表查询\n    \"\"\"\n    ticket = models.ForeignKey(Ticket, null=True, blank=True, on_delete=models.SET_NULL)\n    username = models.CharField('关系人', max_length=100)\n    in_process = models.BooleanField('待处理中', default=False)\n    worked = models.BooleanField('处理过', default=False)\n\n    def __str__(self):\n        return self.username\n\n    class Meta:\n        verbose_name = '工单关系人'\n        verbose_name_plural = verbose_name\n"
  },
  {
    "path": "backend/tickets/serializers.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom tickets.models import *\nfrom workflows.models import *\nfrom rest_framework import serializers\nfrom utils.index import gen_time_pid\nimport json\n\n\nclass TicketReadSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Ticket\n        fields = '__all__'\n        depth = 1\n\n\nclass TicketSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Ticket\n        fields = '__all__'\n\n    def create(self, validated_data):\n        cur_user = self.context['request'].user\n\n        workflow = validated_data[\"workflow\"]\n        transition = validated_data[\"transition\"]\n        customfield_list = validated_data[\"customfield\"]\n\n        # save ticket\n        validated_data[\"sn\"] = gen_time_pid(workflow.ticket_sn_prefix)\n        ticket = Ticket.objects.create(**validated_data)\n\n        # save ticketlog\n        ticketlog = dict()\n        ticketlog[\"ticket\"] = ticket\n        ticketlog[\"suggestion\"] = \"没啥意见\"\n        ticketlog[\"state\"] = transition.source_state\n        ticketlog[\"transition\"] = transition\n        ticketlog[\"participant\"] = cur_user\n        TicketFlowLog.objects.create(**ticketlog)\n\n        # save customfield\n        print(ticket.create_time)\n        field_models = []\n        for item in json.loads(customfield_list):\n            if item['field_key'] == \"create_user\":\n                field_models.append(\n                    TicketCustomField(ticket=ticket, customfield_id=int(item['customfield']),\n                                      field_value=ticket.create_user),\n                )\n            elif item['field_key'] == \"create_time\":\n                field_models.append(\n                    TicketCustomField(ticket=ticket, customfield_id=int(item['customfield']),\n                                      field_value=ticket.create_time),\n                )\n            elif item['field_key'] == \"group\":\n                field_models.append(\n                    TicketCustomField(ticket=ticket, customfield_id=int(item['customfield']),\n                                      field_value=ticket.create_user.group),\n                )\n            elif item['field_key'] == \"id\":\n                field_models.append(\n                    TicketCustomField(ticket=ticket, customfield_id=int(item['customfield']),\n                                      field_value=ticket.create_user.id),\n                )\n            else:\n                field_models.append(\n                    TicketCustomField(ticket=ticket, customfield_id=int(item['customfield']),\n                                      field_value=item['field_value'])\n                )\n        TicketCustomField.objects.bulk_create(field_models)\n\n        # save ticketuser\n        TicketUser.objects.create(ticket=ticket, username=cur_user, worked=True)\n        TicketUser.objects.create(ticket=ticket, username=ticket.participant, in_process=True)\n\n        return ticket\n\n    def update(self, instance, validated_data):\n        cur_user = self.context['request'].user\n        instance.name = validated_data.get('name', instance.name)\n        instance.workflow = validated_data.get('workflow', instance.workflow)\n        instance.sn = validated_data.get('sn', instance.sn)\n        instance.state = validated_data.get('state', instance.state)\n        instance.create_user = validated_data.get('create_user', instance.create_user)\n        instance.participant = validated_data.get('participant', instance.participant)\n        instance.transition = validated_data.get('transition', instance.transition)\n        instance.customfield = validated_data.get('customfield', instance.customfield)\n        instance.memo = validated_data.get('memo', instance.memo)\n\n        # update relation\n        relation = instance.relation.split(',')\n        relation.append(validated_data['relation'])\n        instance.relation = ','.join(set(relation))\n        instance.save()\n\n        # save ticketlog\n        ticketlog = dict()\n        ticketlog[\"ticket\"] = instance\n        ticketlog[\"suggestion\"] = instance.memo\n        ticketlog[\"state\"] = instance.transition.source_state\n        ticketlog[\"transition\"] = instance.transition\n        ticketlog[\"participant\"] = cur_user\n        TicketFlowLog.objects.create(**ticketlog)\n\n        # save customfield\n        customfield_list = json.loads(instance.customfield)\n        for item in customfield_list:\n            TicketCustomField.objects.filter(id=item[\"id\"]).update(field_value=item[\"field_value\"])\n\n        # save ticketuser\n        TicketUser.objects.create(ticket=instance, username=cur_user, worked=True)\n        TicketUser.objects.create(ticket=instance, username=instance.participant, in_process=True)\n        return instance\n\n\nclass TicketFlowLogReadSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = TicketFlowLog\n        fields = '__all__'\n        depth = 2\n\n\nclass TicketFlowLogSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = TicketFlowLog\n        fields = '__all__'\n\n\nclass TicketCustomFieldReadSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = TicketCustomField\n        fields = '__all__'\n        depth = 1\n\n\nclass TicketCustomFieldSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = TicketCustomField\n        fields = '__all__'\n\n\nclass TicketUserSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = TicketUser\n        fields = '__all__'\n"
  },
  {
    "path": "backend/tickets/urls.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\n\nfrom django.conf.urls import url, include\nfrom rest_framework import routers\nfrom tickets.views import TicketViewSet, TicketFlowLogViewSet, TicketCustomFieldViewSet, TicketUserViewSet\n\nrouter = routers.DefaultRouter()\n\nrouter.register(r'ticket', TicketViewSet)\nrouter.register('ticketflowlog', TicketFlowLogViewSet)\nrouter.register(r'ticketcustomfield', TicketCustomFieldViewSet)\nrouter.register(r'ticketuser', TicketUserViewSet)\n\nurlpatterns = [\n]\n\nurlpatterns += router.urls\n"
  },
  {
    "path": "backend/tickets/views.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom tickets.serializers import *\nfrom tickets.filters import *\nfrom common.views import ModelViewSet, FKModelViewSet, JsonResponse, BulkModelMixin\n\n\nclass TicketViewSet(BulkModelMixin):\n    queryset = Ticket.objects.all()\n    serializer_class = TicketSerializer\n    filterset_class = TicketFilter\n    search_fields = ['name']\n    ordering_fields = ['state']\n\n    def get_serializer_class(self):\n        if self.action in ['list', 'retrieve'] or self.resultData:\n            return TicketReadSerializer\n        return TicketSerializer\n\n    def get_queryset(self):\n        try:\n            user = User.objects.get(username=self.request.user)\n            if user.is_admin:\n                return Ticket.objects.all()\n            else:\n                return Ticket.objects.filter(relation__icontains=self.request.user).distinct()\n        except Exception as e:\n            print(e)\n            return Ticket.objects.all()\n\n\nclass TicketFlowLogViewSet(BulkModelMixin):\n    queryset = TicketFlowLog.objects.all()\n    serializer_class = TicketFlowLogSerializer\n    search_fields = ['ticket']\n    filter_fields = ['ticket', 'state']\n\n    def get_serializer_class(self):\n        if self.action in ['list', 'retrieve'] or self.resultData:\n            return TicketFlowLogReadSerializer\n        return TicketFlowLogSerializer\n\n\nclass TicketCustomFieldViewSet(BulkModelMixin):\n    queryset = TicketCustomField.objects.all()\n    serializer_class = TicketCustomFieldSerializer\n    filter_fields = ['ticket', 'customfield']\n\n    def get_serializer_class(self):\n        if self.action in ['list', 'retrieve'] or self.resultData:\n            return TicketCustomFieldReadSerializer\n        return TicketCustomFieldSerializer\n\n\nclass TicketUserViewSet(BulkModelMixin):\n    queryset = TicketUser.objects.all()\n    serializer_class = TicketUserSerializer\n    search_fields = ['username']\n    filter_fields = ['username', 'in_process', 'worked']\n"
  },
  {
    "path": "backend/tools/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\n"
  },
  {
    "path": "backend/tools/filesize.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nimport math\n\n\ndef convert_size(size_bytes):\n    if size_bytes == 0:\n        return \"0B\"\n    size_name = (\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\")\n    i = int(math.floor(math.log(size_bytes, 1024)))\n    p = math.pow(1024, i)\n    s = round(size_bytes / p, 2)\n    return \"%s %s\" % (s, size_name[i])\n"
  },
  {
    "path": "backend/tools/migrations/0001_initial.py",
    "content": "# Generated by Django 3.0.3 on 2021-06-27 00:05\n\nfrom django.db import migrations, models\nimport tools.storage\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='FileUpload',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('file', models.FileField(blank=True, upload_to='./tmp', verbose_name='上传文件')),\n            ],\n            options={\n                'verbose_name': '文件上传',\n                'verbose_name_plural': '文件上传',\n            },\n        ),\n        migrations.CreateModel(\n            name='RequestEvent',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('url', models.CharField(db_index=True, max_length=255, verbose_name='请求URI')),\n                ('method', models.CharField(db_index=True, max_length=20, verbose_name='请求方法')),\n                ('query_string', models.TextField(verbose_name='请求内容')),\n                ('user', models.CharField(max_length=255, null=True, verbose_name='用户')),\n                ('remote_ip', models.CharField(db_index=True, max_length=50, null=True, verbose_name='请求IP')),\n            ],\n            options={\n                'verbose_name': '请求事件',\n                'verbose_name_plural': '请求事件',\n                'ordering': ['-create_time'],\n            },\n        ),\n        migrations.CreateModel(\n            name='SimpleModel',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('name', models.CharField(max_length=255, unique=True, verbose_name='名称')),\n            ],\n        ),\n        migrations.CreateModel(\n            name='Upload',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('username', models.CharField(max_length=20, verbose_name='上传用户')),\n                ('file', models.FileField(blank=True, upload_to=tools.storage.PathAndRename('./'), verbose_name='上传文件')),\n                ('archive', models.CharField(blank=True, default='其他', max_length=201, null=True, verbose_name='文件归档')),\n                ('filename', models.CharField(blank=True, max_length=201, null=True, verbose_name='文件名')),\n                ('filepath', models.CharField(blank=True, max_length=201, null=True, verbose_name='文件路径')),\n                ('type', models.CharField(blank=True, max_length=100, null=True, verbose_name='文件类型')),\n                ('size', models.CharField(blank=True, max_length=20, null=True, verbose_name='文件大小')),\n            ],\n            options={\n                'verbose_name': '文件上传',\n                'verbose_name_plural': '文件上传',\n            },\n        ),\n    ]\n"
  },
  {
    "path": "backend/tools/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "backend/tools/models.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom django.db import models\nfrom common.models import BaseModel\nfrom tools.filesize import convert_size\nfrom tools.storage import PathAndRename\nimport os\n\n\nclass Upload(BaseModel):\n    username = models.CharField(max_length=20, verbose_name=u'上传用户')\n    file = models.FileField(upload_to=PathAndRename(\"./\"), blank=True, verbose_name=u'上传文件')\n    archive = models.CharField(max_length=201, default=u'其他', null=True, blank=True, verbose_name=u'文件归档')\n    filename = models.CharField(max_length=201, null=True, blank=True, verbose_name=u'文件名')\n    filepath = models.CharField(max_length=201, null=True, blank=True, verbose_name=u'文件路径')\n    type = models.CharField(max_length=100, null=True, blank=True, verbose_name=u'文件类型')\n    size = models.CharField(max_length=20, null=True, blank=True, verbose_name=u'文件大小')\n\n    def save(self, *args, **kwargs):\n        from re import sub\n        self.size = '{}'.format(convert_size(self.file.size))\n        filename = os.path.splitext(self.file.name)\n        self.filename = '{}-{}{}'.format(sub('\\W+', '', filename[0]), self.create_time, filename[1]).replace(' ', '_')\n        self.filepath = '{}/{}'.format(self.archive, self.filename)\n        super(Upload, self).save(*args, **kwargs)\n\n    def __str__(self):\n        return self.filepath\n\n    class Meta:\n        verbose_name = u'文件上传'\n        verbose_name_plural = u'文件上传'\n\n\nclass FileUpload(models.Model):\n    file = models.FileField(upload_to=(\"./tmp\"), blank=True, verbose_name=u'上传文件')\n\n    class Meta:\n        verbose_name = u'文件上传'\n        verbose_name_plural = u'文件上传'\n\n\nclass RequestEvent(BaseModel):\n    url = models.CharField(max_length=255, null=False, db_index=True, verbose_name='请求URI')\n    method = models.CharField(max_length=20, null=False, db_index=True, verbose_name='请求方法')\n    query_string = models.TextField(verbose_name='请求内容')\n    user = models.CharField(max_length=255, null=True, verbose_name='用户')\n    remote_ip = models.CharField(max_length=50, null=True, db_index=True, verbose_name='请求IP')\n\n    class Meta:\n        ordering = ['-create_time']\n        verbose_name = '请求事件'\n        verbose_name_plural = verbose_name\n\n\nclass SimpleModel(models.Model):\n    name = models.CharField(max_length=255, unique=True, verbose_name='名称')\n"
  },
  {
    "path": "backend/tools/serializers.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom rest_framework import serializers\nfrom tools.models import *\n\n\nclass UploadSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Upload\n        fields = '__all__'\n\n\nclass FileUploadSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = FileUpload\n        fields = '__all__'\n\n\nclass RequestEventSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = RequestEvent\n        fields = '__all__'\n\nclass SimpleSerializer(serializers.ModelSerializer):\n    class Meta(object):\n        model = SimpleModel\n        fields = '__all__'\n"
  },
  {
    "path": "backend/tools/storage.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nimport os\nfrom django.utils.deconstruct import deconstructible\nfrom re import sub\n\n\n@deconstructible\nclass PathAndRename(object):\n    def __init__(self, sub_path):\n        self.path = sub_path\n\n    def __call__(self, instance, file):\n        filename = os.path.splitext(file)\n        last_filename = \"%s-%s%s\" % (sub('\\W+', '', filename[0]), instance.create_time, filename[1])\n        return os.path.join(self.path, instance.archive, last_filename)\n"
  },
  {
    "path": "backend/tools/urls.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\n\nfrom django.conf.urls import url\nfrom rest_framework import routers\nfrom tools.views import UploadViewSet, FileUploadViewSet, RequestEventViewSet, SimpleViewSet\n\nrouter = routers.DefaultRouter()\n\nrouter.register(r'upload', UploadViewSet)\nrouter.register(r'fileupload', FileUploadViewSet)\nrouter.register(r'audit', RequestEventViewSet)\nrouter.register('simple', SimpleViewSet)\n\nurlpatterns = [\n]\n\nurlpatterns += router.urls\n"
  },
  {
    "path": "backend/tools/views.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom rest_framework import viewsets, permissions\nfrom tools.models import *\nfrom tools.serializers import *\nfrom common.views import ModelViewSet, FKModelViewSet, JsonResponse, BulkModelMixin\n\n\nclass UploadViewSet(ModelViewSet):\n    queryset = Upload.objects.all().order_by(\"-create_time\")\n    serializer_class = UploadSerializer\n    filter_fields = ('username', 'type',)\n\n\nclass FileUploadViewSet(ModelViewSet):\n    permission_classes = [permissions.AllowAny]\n    queryset = FileUpload.objects.all()\n    serializer_class = FileUploadSerializer\n\n\nclass RequestEventViewSet(ModelViewSet):\n    queryset = RequestEvent.objects.all()\n    serializer_class = RequestEventSerializer\n    search_fields = ['url', 'query_string', 'user', 'remote_ip']\n    filter_fields = ['method']\n\n\nclass SimpleViewSet(BulkModelMixin):\n    queryset = SimpleModel.objects.all()\n    serializer_class = SimpleSerializer\n    permission_classes = [permissions.AllowAny]\n    filter_fields = ['id', 'name']\n\n"
  },
  {
    "path": "backend/utils/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\n"
  },
  {
    "path": "backend/utils/get_realip.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\n\nimport requests\nimport socket\nimport json\n\n#获取外网ip信息\noutput = requests.get('https://ifconfig.me/all.json').json()\n\n# 获取本机计算机ip\ndef get_local_ip():\n    try:\n        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n        s.connect(('8.8.8.8', 80))\n        ip = s.getsockname()[0]\n    finally:\n        s.close()\n\n    return ip\n\noutput['local_ip'] = get_local_ip()\n\n## output\n\"\"\"\n{\n  \"ip_addr\": \"203.177.78.226\",\n  \"remote_host\": \"unavailable\",\n  \"user_agent\": \"python-requests/2.22.0\",\n  \"port\": 38542,\n  \"method\": \"GET\",\n  \"encoding\": \"gzip, deflate\",\n  \"mime\": \"*/*\",\n  \"via\": \"1.1 google\",\n  \"forwarded\": \"203.177.78.226, 216.239.32.21\"\n  \"local_ip\": \"172.16.51.115\"\n}\"\"\"\n\nwith open('d:/ooxx.log', 'a+') as fn:\n    print(output)\n    fn.write(json.dumps(output))\n"
  },
  {
    "path": "backend/utils/index.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nfrom datetime import datetime, timedelta\nimport time\n\n\ndef gen_time_pid(prefix):\n    pid = '{}_{}'.format(prefix, datetime.now().strftime('%Y%m%d%H%M%S') + str(time.time()).replace('.', '')[-3:])\n    return pid\n\n\ndef diff_times_in_seconds(t1, t2):\n    h1, m1, s1 = t1.hour, t1.minute, t1.second\n    h2, m2, s2 = t2.hour, t2.minute, t2.second\n    t1_secs = s1 + 60 * (m1 + 60 * h1)\n    t2_secs = s2 + 60 * (m2 + 60 * h2)\n    tc = str(timedelta(seconds=(t2_secs - t1_secs)))\n    return tc\n\n\nif __name__ == '__main__':\n    prefix = 'xxoo'\n    print(gen_time_pid(prefix))\n"
  },
  {
    "path": "backend/utils/mysql.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nimport MySQLdb\n\n\nclass MYSQL:\n    def __init__(self, db, sql):\n        self.sql = sql\n        self.conn = MySQLdb.connect(\n            host=db[\"host\"],\n            port=db[\"port\"],\n            user=db[\"user\"],\n            passwd=db[\"passwd\"],\n            db=db[\"db\"],\n            charset='utf8')\n        self.cursor = self.conn.cursor()\n\n    def insert(self):\n        self.cursor.execute(self.sql)\n        self.conn.commit()\n        self.cursor.close()\n        self.conn.close()\n        return True\n\n    def select(self):\n        self.cursor.execute(self.sql)\n        alldata = self.cursor.fetchall()\n        self.cursor.close()\n        self.conn.close()\n        return alldata\n\n    def update(self):\n        self.cursor.execute(self.sql)\n        self.conn.commit()\n        self.cursor.close()\n        self.conn.close()\n        return True\n\n\nif __name__ == '__main__':\n    xxljob_info = {\n        \"host\": \"localhost\",\n        \"port\": 3306,\n        \"user\": \"root\",\n        \"passwd\": \"TY%pwd123\",\n        \"db\": \"xxl_job\",\n    }\n    jobapi = MYSQL(xxljob_info)\n    sql = \"select * from xxl_job_group\"\n    data = jobapi.insert(sql)\n    rep_data = []\n    for item in data:\n        json_data = {\n            \"id\": item[0],\n            \"app_name\": item[1],\n            \"title\": item[2],\n            \"order\": item[3],\n            \"address_type\": item[4],\n            \"address_list\": item[5],\n        }\n        rep_data.append(json_data)\n    print(rep_data)\n"
  },
  {
    "path": "backend/utils/sendmail.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nimport sys\nimport smtplib\nfrom email.mime.text import MIMEText\nfrom email.mime.multipart import MIMEMultipart\nfrom omsBackend.settings import MAIL_ACOUNT\n\n# 设置服务器名称、用户名、密码以及邮件后缀\nmail_host = MAIL_ACOUNT[\"mail_host\"]\nmail_user = MAIL_ACOUNT[\"mail_user\"]\nmail_pass = MAIL_ACOUNT[\"mail_pass\"]\nmail_postfix = MAIL_ACOUNT[\"mail_postfix\"]\n\n\n# 发送邮件函数\ndef send_mail(to_list, cc_list, sub, content):\n    me = mail_user + \"<\" + mail_user + \"@\" + mail_postfix + \">\"\n    # f = open(context)\n    # msg = MIMEText(f.read(),_charset=\"utf-8\")\n    # f.close()\n    # msg = MIMEText(context)\n    msg = MIMEMultipart('alternative')\n    msg['Subject'] = sub\n    msg['From'] = me\n    msg['To'] = to_list\n    msg['Cc'] = cc_list\n    list = msg['Cc'].split(',')\n    list.append(msg['To'])\n    context = MIMEText(content, _subtype='html', _charset='utf-8')  # 解决乱码\n    msg.attach(context)\n    try:\n        send_smtp = smtplib.SMTP()\n        send_smtp.connect(mail_host, 587)\n        send_smtp.starttls()\n        send_smtp.login(mail_user, mail_pass)\n\n        send_smtp.sendmail(me, list, msg.as_string())\n        send_smtp.close()\n        return {\"code\": 'success', \"msg\": \"通知邮件发送成功\"}\n    except Exception as e:\n        print(e)\n        return {\"code\": 'error', \"msg\": \"通知邮件发送失败\"}\n\n\nif __name__ == '__main__':\n    to_list = sys.argv[1]  # 收件人列表   '111@126.com'\n    cc_list = sys.argv[2]  # 抄送人列表   '111@126.com;222@126.com;'\n    sub = sys.argv[3]\n    context = sys.argv[4]\n    if send_mail(to_list, cc_list, sub, context):\n        print({\"code\": 'success', \"msg\": \"通知邮件发送成功\"})\n    else:\n        print({\"code\": 'error', \"msg\": \"通知邮件发送失败\"})"
  },
  {
    "path": "backend/utils/sendskype.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\n# 登录skype\nfrom skpy import Skype\n\n# skype账号\nSK_ACOUNT = {\n    'sk_user': 'itimor@126.com',\n    'sk_pass': 'xxx'\n}\nSK = Skype(SK_ACOUNT[\"sk_user\"], SK_ACOUNT[\"sk_pass\"])\n\n\ndef skype_bot(user, content):\n    chat = SK.chats[user]\n    chat.sendMsg(content)\n\n\nif __name__ == '__main__':\n    skypeid = 'live:dafaricky123'\n    user = '8:' + skypeid  # skypeid 前面需要加 8\n    skype_bot(user, \"hello,gay,你个逗比，不加好友，发你妹的消息啊\")\n"
  },
  {
    "path": "backend/utils/test.py",
    "content": "#!/bin/bash\n\nid=$1\necho $id\ncp -r site-10 site-$id\ncd site-$id\nsed -i \"s/10/$id/\" *"
  },
  {
    "path": "backend/utils/time.py",
    "content": "# -*- coding: utf-8 -*-\n# author: timor\n\nimport time\nimport datetime\n\n\ndef utc2local(utc_st):\n    \"\"\"\n    UTC时间转本地时间 +8:00\n    \"\"\"\n    now_stamp = time.time()\n    local_time = datetime.datetime.fromtimestamp(now_stamp)\n    utc_time = datetime.datetime.utcfromtimestamp(now_stamp)\n    offset = local_time - utc_time\n    local_st = utc_st + offset\n    return local_st\n\n\ndef local2utc(local_st):\n    \"\"\"\n    本地时间转UTC时间 -8:00\n    \"\"\"\n    time_struct = time.mktime(local_st.timetuple())\n    utc_st = datetime.datetime.utcfromtimestamp(time_struct)\n    return utc_st\n\n\n# '2015-08-28 16:43:37.283' --> 1440751417.283\n# 或者 '2015-08-28 16:43:37' --> 1440751417.0\ndef string2timestamp(strValue):\n    try:\n        d = datetime.datetime.strptime(strValue, \"%Y-%m-%d %H:%M:%S.%f\")\n        t = d.timetuple()\n        timeStamp = int(time.mktime(t))\n        timeStamp = float(str(timeStamp) + str(\"%06d\" % d.microsecond)) / 1000000\n        return int(timeStamp)\n    except ValueError as e:\n        d = datetime.datetime.strptime(strValue, \"%Y-%m-%d %H:%M:%S\")\n        t = d.timetuple()\n        timeStamp = int(time.mktime(t))\n        timeStamp = float(str(timeStamp) + str(\"%06d\" % d.microsecond)) / 1000000\n        return int(timeStamp)\n\n\n# 1440751417.283 --> '2015-08-28 16:43:37.283'\ndef timestamp2string(timeStamp):\n    try:\n        d = datetime.datetime.fromtimestamp(timeStamp)\n        str1 = d.strftime(\"%Y-%m-%d %H:%M:%S.%f\")\n        # 2015-08-28 16:43:37.283000'\n        return str1\n    except Exception as e:\n        pass\n"
  },
  {
    "path": "backend/utils/verifys.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nimport re\nimport IPy\n\n\ndef is_valid_domain(value):\n    domain_pattern = re.compile(\n        r'^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|'\n        r'([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|'\n        r'([a-zA-Z0-9][-_.a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\\.'\n        r'([a-zA-Z]{2,13}|[a-zA-Z0-9-]{2,30}.[a-zA-Z]{2,3})$'\n    )\n    return True if domain_pattern.match(value) else False\n\n\ndef is_domain(domain):\n    domain_regex = re.compile(\n        r'(?:[A-Z0-9_](?:[A-Z0-9-_]{0,247}[A-Z0-9])?\\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))\\Z',\n        re.IGNORECASE)\n    return True if domain_regex.match(domain) else False\n\n\ndef is_ip(address):\n    print(address)\n    try:\n        IPy.IP(address)\n        return True\n    except Exception as e:\n        print(e)\n        return False\n\n\nif __name__ == '__main__':\n    print(is_valid_domain('https://aa.www.baidu.com'))\n    print(is_ip('22.2.2.256'))\n"
  },
  {
    "path": "backend/workflows/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\n"
  },
  {
    "path": "backend/workflows/admin.py",
    "content": "from django.contrib import admin\nfrom workflows.models import *\n\nadmin.site.register(WorkflowBpmn)\n"
  },
  {
    "path": "backend/workflows/management/__init__.py",
    "content": ""
  },
  {
    "path": "backend/workflows/management/commands/__init__.py",
    "content": ""
  },
  {
    "path": "backend/workflows/management/commands/init_leave.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom django.core.management.base import BaseCommand, CommandError\nfrom django.contrib.auth.hashers import make_password\n\nfrom workflows.models import *\nfrom systems.models import *\n\n\nclass Command(BaseCommand):\n    help = '假期工作流'\n\n    def handle(self, *args, **options):\n        self.stdout.write(self.style.SUCCESS('############ 配置工作流用户角色 ###########'))\n        topgroup = Group.objects.get(name='top', code='top')\n\n        group_ops = Group.objects.create(name='运维', code='ops', sequence=1, parent=topgroup)\n        group_dev = Group.objects.create(name='开发', code='dev', sequence=2, parent=topgroup)\n        group_hr = Group.objects.create(name='人事', code='hr', sequence=3, parent=topgroup)\n\n        self.stdout.write(self.style.SUCCESS('############ 初始化角色 ###########'))\n        toprole = Role.objects.get(name='top', code='top')\n\n        role_ops_tl = Role.objects.create(name='运维经理', code='ops_tl', sequence=1, group=group_ops, parent=toprole)\n        role_ops = Role.objects.create(name='运维', code='ops', sequence=1, group=group_ops, parent=toprole)\n        role_dev_tl = Role.objects.create(name='开发经理', code='dev_tl', sequence=2, group=group_dev, parent=toprole)\n        role_dev = Role.objects.create(name='开发', code='dev', sequence=2, group=group_dev, parent=toprole)\n        role_hr_tl = Role.objects.create(name='人事经理', code='hr_tl', sequence=3, group=group_hr, parent=toprole)\n        role_hr = Role.objects.create(name='人事', code='hr', sequence=3, group=group_hr, parent=toprole)\n\n        self.stdout.write(self.style.SUCCESS('############ 初始化用户 ###########'))\n        ops_tl = User.objects.create(username='ops_tl', password=make_password(\"123456\"), realname=\"青龙\",\n                                     group=group_ops)\n        ops_tl.roles.add(role_ops_tl)\n        ops = User.objects.create(username='ops', password=make_password(\"123456\"), realname=\"白虎\", group=group_ops)\n        ops.roles.add(role_ops)\n        dev_tl = User.objects.create(username='dev_tl', password=make_password(\"123456\"), realname=\"朱雀\",\n                                     group=group_dev)\n        dev_tl.roles.add(role_dev_tl)\n        dev = User.objects.create(username='dev', password=make_password(\"123456\"), realname=\"玄武\", group=group_dev)\n        dev.roles.add(role_dev)\n        hr_tl = User.objects.create(username='hr_tl', password=make_password(\"123456\"), realname=\"霸下\", group=group_hr)\n        hr_tl.roles.add(role_hr_tl)\n        hr = User.objects.create(username='hr', password=make_password(\"123456\"), realname=\"青鸾\", group=group_hr)\n        hr.roles.add(role_hr)\n\n        self.stdout.write(self.style.SUCCESS('############ 请假工作流 ###########'))\n        ## 工作流类型\n        ad_type = WorkflowType.objects.create(name='行政', code='ad', order_id=1)\n\n        ## 工作流\n        leave_wf = Workflow.objects.create(name='请假单', type=ad_type, ticket_sn_prefix='leave')\n\n        ## 工作流字段\n        # 建立内置字段\n        CustomField.objects.create(field_name=\"申请人\", order_id=1, field_attribute=True, field_type=1,\n                                   field_key=\"create_user\", workflow=leave_wf)\n        CustomField.objects.create(field_name=\"申请时间\", order_id=2, field_attribute=True, field_type=6,\n                                   field_key=\"create_time\", workflow=leave_wf)\n        CustomField.objects.create(field_name=\"部门\", order_id=3, field_attribute=True, field_type=1, field_key=\"group\",\n                                   workflow=leave_wf)\n        CustomField.objects.create(field_name=\"工号\", order_id=4, field_attribute=True, field_type=2, field_key=\"id\",\n                                   workflow=leave_wf)\n        # 建立扩展字段\n        c1 = CustomField.objects.create(field_name=\"请假时间\", order_id=10, field_type=7, field_key=\"start_end_time\",\n                                        workflow=leave_wf)\n        c2 = CustomField.objects.create(field_name=\"请假类型\", order_id=30, field_type=9, field_key=\"type\",\n                                        field_choice='{\"1\":\"病假\", \"2\":\"产假\"}', workflow=leave_wf)\n        c3 = CustomField.objects.create(field_name=\"事由说明\", order_id=50, field_type=8, field_key=\"memo\",\n                                        workflow=leave_wf)\n        c4 = CustomField.objects.create(field_name=\"领导审批\", order_id=60, field_type=9, field_key=\"leader_radio\",\n                                        field_choice='{\"1\":\"同意\", \"2\":\"不同意\"}', workflow=leave_wf)\n        c5 = CustomField.objects.create(field_name=\"人事审批\", order_id=80, field_type=9, field_key=\"hr_radio\",\n                                        field_choice='{\"1\":\"同意\", \"2\":\"不同意\"}', workflow=leave_wf)\n\n        # 建立初始和结束状态\n        s1 = State.objects.create(name=\"开始\", order_id=1, state_type=1, is_hidden=True, participant_type='none',\n                                  workflow=leave_wf)\n        s2 = State.objects.create(name=\"关闭\", order_id=99, state_type=2, is_hidden=True, participant_type='none',\n                                  workflow=leave_wf)\n        # 建立流转状态\n        s3 = State.objects.create(name=\"申请人-编辑中\", order_id=2, participant_type='none', workflow=leave_wf)\n        s3.fields.add(c1, c2, c3)\n        s4 = State.objects.create(name=\"领导-审批中\", order_id=3, participant_type='role', workflow=leave_wf)\n        s4.fields.add(c4)\n        s4.role_participant.add(role_ops_tl, role_dev_tl, role_hr_tl)\n        s5 = State.objects.create(name=\"人事-审批中\", order_id=4, participant_type='group', workflow=leave_wf)\n        s5.fields.add(c5)\n        s5.group_participant.add(group_hr)\n        s6 = State.objects.create(name=\"结束\", order_id=98, state_type=2, participant_type='none', workflow=leave_wf)\n\n        # 建立工作流步骤\n        Transition.objects.create(name=0, source_state=s1, dest_state=s3, attribute_type=0, workflow=leave_wf)\n        Transition.objects.create(name=1, source_state=s1, dest_state=s4, attribute_type=1, workflow=leave_wf)\n\n        Transition.objects.create(name=0, source_state=s3, dest_state=s3, attribute_type=0, workflow=leave_wf)\n        Transition.objects.create(name=1, source_state=s3, dest_state=s4, attribute_type=1, workflow=leave_wf)\n        Transition.objects.create(name=3, source_state=s3, dest_state=s6, attribute_type=3, workflow=leave_wf)\n\n        Transition.objects.create(name=2, source_state=s4, dest_state=s3, attribute_type=2, workflow=leave_wf)\n        Transition.objects.create(name=1, source_state=s4, dest_state=s5, attribute_type=1, workflow=leave_wf)\n\n        Transition.objects.create(name=2, source_state=s5, dest_state=s3, attribute_type=2, workflow=leave_wf)\n        Transition.objects.create(name=4, source_state=s5, dest_state=s2, attribute_type=5, workflow=leave_wf)\n\n        self.stdout.write(self.style.SUCCESS('请假工作流完成'))\n\n        self.stdout.write(self.style.SUCCESS('############ 发布工作流 ###########'))\n        ## 工作流类型\n        it_type = WorkflowType.objects.create(name='技术', code='it', order_id=2)\n\n        ## 工作流\n        deploy_wf = Workflow.objects.create(name='发布单', type=it_type, ticket_sn_prefix='deploy')\n\n        ## 工作流字段\n        # 建立内置字段\n        CustomField.objects.create(field_name=\"申请人\", order_id=1, field_attribute=True, field_type=1,\n                                   field_key=\"create_user\", workflow=deploy_wf)\n        CustomField.objects.create(field_name=\"申请时间\", order_id=2, field_attribute=True, field_type=6,\n                                   field_key=\"create_time\", workflow=deploy_wf)\n        CustomField.objects.create(field_name=\"部门\", order_id=3, field_attribute=True, field_type=1, field_key=\"group\",\n                                   workflow=deploy_wf)\n        CustomField.objects.create(field_name=\"工号\", order_id=4, field_attribute=True, field_type=2, field_key=\"id\",\n                                   workflow=deploy_wf)\n        # 建立扩展字段\n        c1 = CustomField.objects.create(field_name=\"发布时间\", order_id=10, field_type=6, field_key=\"start_time\",\n                                        workflow=deploy_wf)\n        c2 = CustomField.objects.create(field_name=\"发布项目\", order_id=30, field_type=9, field_key=\"type\",\n                                        field_choice='{\"1\":\"前端\", \"2\":\"后端\"}', workflow=deploy_wf)\n        c3 = CustomField.objects.create(field_name=\"发布内容\", order_id=50, field_type=8, field_key=\"memo\",\n                                        workflow=deploy_wf)\n        c4 = CustomField.objects.create(field_name=\"领导审批\", order_id=60, field_type=9, field_key=\"leader_radio\",\n                                        field_choice='{\"1\":\"同意\", \"2\":\"不同意\"}', workflow=deploy_wf)\n        c5 = CustomField.objects.create(field_name=\"运维执行\", order_id=80, field_type=9, field_key=\"ops_radio\",\n                                        field_choice='{\"1\":\"已执行\", \"2\":\"未执行\"}', workflow=deploy_wf)\n\n        # 建立初始和结束状态\n        s1 = State.objects.create(name=\"开始\", order_id=1, state_type=1, is_hidden=True, participant_type='none',\n                                  workflow=deploy_wf)\n        s2 = State.objects.create(name=\"关闭\", order_id=99, state_type=2, is_hidden=True, participant_type='none',\n                                  workflow=deploy_wf)\n        # 建立流转状态\n        s3 = State.objects.create(name=\"申请人-编辑中\", order_id=2, participant_type='none', workflow=deploy_wf)\n        s3.fields.add(c1, c2, c3)\n        s4 = State.objects.create(name=\"领导-审批中\", order_id=3, participant_type='role', workflow=deploy_wf)\n        s4.fields.add(c4)\n        s4.role_participant.add(role_ops_tl, role_dev_tl, role_hr_tl)\n        s5 = State.objects.create(name=\"运维-执行中\", order_id=4, participant_type='group', workflow=deploy_wf)\n        s5.fields.add(c5)\n        s5.group_participant.add(group_hr)\n        s6 = State.objects.create(name=\"结束\", order_id=98, state_type=2, participant_type='none', workflow=deploy_wf)\n\n        # 建立工作流步骤\n        Transition.objects.create(name=0, source_state=s1, dest_state=s3, attribute_type=0, workflow=deploy_wf)\n        Transition.objects.create(name=1, source_state=s1, dest_state=s4, attribute_type=1, workflow=deploy_wf)\n\n        Transition.objects.create(name=0, source_state=s3, dest_state=s3, attribute_type=0, workflow=deploy_wf)\n        Transition.objects.create(name=1, source_state=s3, dest_state=s4, attribute_type=1, workflow=deploy_wf)\n        Transition.objects.create(name=3, source_state=s3, dest_state=s6, attribute_type=3, workflow=deploy_wf)\n\n        Transition.objects.create(name=2, source_state=s4, dest_state=s3, attribute_type=2, workflow=deploy_wf)\n        Transition.objects.create(name=1, source_state=s4, dest_state=s5, attribute_type=1, workflow=deploy_wf)\n\n        Transition.objects.create(name=2, source_state=s5, dest_state=s3, attribute_type=2, workflow=deploy_wf)\n        Transition.objects.create(name=4, source_state=s5, dest_state=s2, attribute_type=5, workflow=deploy_wf)\n\n        self.stdout.write(self.style.SUCCESS('############ 初始化角色权限 ###########'))\n        menus = [34, 35, 36, 37, 38, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,\n                 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96]\n        perms = [52, 56, 60, 92, 96, 48, 44, 52, 73, 74, 75, 76, 85, 86, 87, 88, 81, 82, 83, 84, 77, 78, 79, 80, 28, 32, 68, 64]\n        menu_obj_list = Menu.objects.filter(id__in=menus)\n        perm_obj_list = Permission.objects.filter(id__in=perms)\n\n        role_ops_tl.menus.add(*menu_obj_list)\n        role_ops_tl.model_perms.add(*perm_obj_list)\n        role_ops.menus.add(*menu_obj_list)\n        role_ops.model_perms.add(*perm_obj_list)\n        role_dev_tl.menus.add(*menu_obj_list)\n        role_dev_tl.model_perms.add(*perm_obj_list)\n        role_dev.menus.add(*menu_obj_list)\n        role_dev.model_perms.add(*perm_obj_list)\n        role_hr_tl.menus.add(*menu_obj_list)\n        role_hr_tl.model_perms.add(*perm_obj_list)\n        role_hr.menus.add(*menu_obj_list)\n        role_hr.model_perms.add(*perm_obj_list)\n        self.stdout.write(self.style.SUCCESS('发布工作流完成'))\n"
  },
  {
    "path": "backend/workflows/management/commands/init_wf.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom django.core.management.base import BaseCommand, CommandError\nfrom systems.models import *\nfrom systems.menus import init_menu\n\n\nclass Command(BaseCommand):\n    help = '初始化工作流'\n\n    def handle(self, *args, **options):\n        topmenu = Menu.objects.get(name='top', code='top')\n        self.stdout.write(self.style.SUCCESS('############ 初始化工作流菜单 ###########'))\n        workflowmenu = Menu.objects.create(name='工作流', code='workflow', curl='/workflow', icon='workflow', sequence=3, type=1, parent=topmenu)\n        menumodel = Menu.objects.create(name='工作流类型', code='wftype', curl='/wftype', icon='wftype', sequence=10, type=2, parent=workflowmenu)\n        init_menu(menumodel)\n        menumodel = Menu.objects.create(name='工作流设计', code='wfset', curl='/wfset', icon='wfset', sequence=20, type=2, parent=workflowmenu)\n        init_menu(menumodel)\n        menumodel = Menu.objects.create(name='工作流配置', code='wfconf', curl='/wfconf/:id', icon='wfconf', sequence=30, type=2, hidden=True, active_menu='/wfset', parent=workflowmenu)\n        init_menu(menumodel)\n        self.stdout.write(self.style.SUCCESS('初始化完成'))\n"
  },
  {
    "path": "backend/workflows/migrations/0001_initial.py",
    "content": "# Generated by Django 3.0.3 on 2021-06-27 00:05\n\nfrom django.conf import settings\nfrom django.db import migrations, models\nimport django.db.models.deletion\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n        ('systems', '0001_initial'),\n        migrations.swappable_dependency(settings.AUTH_USER_MODEL),\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='CustomField',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('field_attribute', models.BooleanField(default=False, verbose_name='字段是否内置')),\n                ('field_type', models.CharField(choices=[(1, '字符串'), (2, '整型'), (3, '浮点型'), (4, '布尔'), (5, '日期'), (6, '日期时间'), (7, '范围日期'), (8, '文本域'), (9, '单选框'), (10, '下拉列表'), (11, '用户名'), (12, '多选框'), (13, '多选下拉'), (14, '多选用户名')], default=0, max_length=1, verbose_name='字段类型')),\n                ('field_key', models.CharField(help_text='字段类型请尽量特殊，避免与系统中关键字冲突', max_length=50, verbose_name='字段标识')),\n                ('field_name', models.CharField(max_length=50, verbose_name='字段名称')),\n                ('order_id', models.IntegerField(default=0, verbose_name='排序')),\n                ('default_value', models.CharField(blank=True, help_text='前端展示时，可以将此内容作为表单中的该字段的默认值', max_length=100, null=True, verbose_name='默认值')),\n                ('field_template', models.TextField(blank=True, default='', help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder', verbose_name='文本域模板')),\n                ('boolean_field_display', models.CharField(blank=True, default='{}', help_text='当为布尔类型时候，可以支持自定义显示形式。{\"1\":\"是\",\"0\":\"否\"}或{\"1\":\"需要\",\"0\":\"不需要\"}，注意数字也需要引号', max_length=100, verbose_name='布尔类型显示名')),\n                ('field_choice', models.CharField(blank=True, default='{}', help_text='radio,checkbox,select,multiselect类型可供选择的选项，格式为json如:{\"1\":\"中国\", \"2\":\"美国\"},注意数字也需要引号', max_length=255, verbose_name='radio、checkbox、select的选项')),\n                ('label', models.CharField(blank=True, default='{}', help_text='自定义标签，json格式，调用方可根据标签自行处理特殊场景逻辑，loonflow只保存文本内容', max_length=100, verbose_name='标签')),\n            ],\n            options={\n                'verbose_name': '工作流自定义字段',\n                'verbose_name_plural': '工作流自定义字段',\n                'ordering': ['order_id'],\n            },\n        ),\n        migrations.CreateModel(\n            name='State',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('name', models.CharField(max_length=50, verbose_name='名称')),\n                ('is_hidden', models.BooleanField(default=False, help_text='设置为True时,获取工单步骤api中不显示此状态(当前处于此状态时除外)', verbose_name='是否隐藏')),\n                ('order_id', models.IntegerField(default=1, verbose_name='状态顺序')),\n                ('state_type', models.CharField(choices=[(0, '普通状态'), (1, '初始状态'), (2, '结束状态')], default=0, max_length=1, verbose_name='状态类型')),\n                ('enable_retreat', models.BooleanField(default=False, help_text='开启后允许工单创建人在此状态直接撤回工单到初始状态', verbose_name='允许撤回')),\n                ('participant_type', models.CharField(choices=[('none', '无处理人'), ('user', '个人'), ('group', '部门'), ('role', '角色')], default='none', max_length=5, verbose_name='参与者类型')),\n                ('fields', models.ManyToManyField(blank=True, to='workflows.CustomField', verbose_name='可编辑字段')),\n                ('group_participant', models.ManyToManyField(blank=True, to='systems.Group', verbose_name='参与组')),\n                ('role_participant', models.ManyToManyField(blank=True, to='systems.Role', verbose_name='参与角色')),\n                ('user_participant', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='参与用户')),\n            ],\n            options={\n                'verbose_name': '工作流状态',\n                'verbose_name_plural': '工作流状态',\n                'ordering': ['order_id'],\n            },\n        ),\n        migrations.CreateModel(\n            name='Workflow',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('name', models.CharField(max_length=50, verbose_name='名称')),\n                ('key', models.CharField(blank=True, max_length=168, verbose_name='流程标识key')),\n                ('ticket_sn_prefix', models.CharField(default='xxoo', max_length=20, verbose_name='工单流水号前缀')),\n                ('status', models.BooleanField(default=True)),\n                ('view_permission_check', models.BooleanField(default=True, help_text='开启后，只允许工单的关联人(创建人、曾经的处理人)有权限查看工单', verbose_name='查看权限校验')),\n                ('limit_expression', models.TextField(blank=True, default='{}', help_text='限制周期({\"period\":24} 24小时), 限制次数({\"count\":1}在限制周期内只允许提交1次), 限制级别({\"level\":1} 针对(1单个用户 2全局)限制周期限制次数,默认特定用户);允许特定人员提交({\"allow_persons\":\"zhangsan,lisi\"}只允许张三提交工单,{\"allow_depts\":\"1,2\"}只允许部门id为1和2的用户提交工单，{\"allow_roles\":\"1,2\"}只允许角色id为1和2的用户提交工单)', verbose_name='限制表达式')),\n                ('display_form_str', models.TextField(blank=True, default='[]', help_text='默认\"[]\"，用于用户只有对应工单查看权限时显示哪些字段,field_key的list的json,如[\"days\",\"sn\"],内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称)，state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称', verbose_name='展现表单字段')),\n                ('title_template', models.CharField(blank=True, default='你有一个待办工单:{title}', help_text='工单字段的值可以作为参数写到模板中，格式如：你有一个待办工单:{title}', max_length=50, null=True, verbose_name='标题模板')),\n                ('roles', models.ManyToManyField(blank=True, related_name='workflow_set', related_query_name='workflow', to='systems.Role', verbose_name='关联角色')),\n            ],\n            options={\n                'verbose_name': '工作流',\n                'verbose_name_plural': '工作流',\n            },\n        ),\n        migrations.CreateModel(\n            name='WorkflowType',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('name', models.CharField(max_length=50, verbose_name='名称')),\n                ('status', models.BooleanField(default=True)),\n                ('code', models.CharField(max_length=32, unique=True, verbose_name='代码')),\n                ('order_id', models.IntegerField(default=1, verbose_name='状态顺序')),\n            ],\n            options={\n                'verbose_name': '工作流类型',\n                'verbose_name_plural': '工作流类型',\n                'ordering': ['order_id'],\n            },\n        ),\n        migrations.CreateModel(\n            name='WorkflowBpmn',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('xml', models.TextField(blank=True, verbose_name='xml数据')),\n                ('svg', models.TextField(blank=True, verbose_name='svg数据')),\n                ('workflow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflows.Workflow', verbose_name='工作流')),\n            ],\n            options={\n                'verbose_name': '工作流bpmn',\n                'verbose_name_plural': '工作流bpmn',\n            },\n        ),\n        migrations.AddField(\n            model_name='workflow',\n            name='type',\n            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflows.WorkflowType', verbose_name='工作流类型'),\n        ),\n        migrations.CreateModel(\n            name='Transition',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),\n                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),\n                ('memo', models.TextField(blank=True, verbose_name='备注')),\n                ('name', models.CharField(choices=[(0, '保存'), (1, '转交下一步'), (2, '驳回'), (3, '撤销'), (4, '关闭')], default=1, max_length=1, verbose_name='名称类型')),\n                ('transition_type', models.CharField(choices=[(0, '常规流转'), (1, '定时器流转')], default=0, max_length=1, verbose_name='流转类型')),\n                ('timer', models.IntegerField(default=0, help_text='流转类型设置为定时器流转时生效,单位秒。处于源状态X秒后如果状态都没有过变化则自动流转到目标状态', verbose_name='定时器(单位秒)')),\n                ('condition_expression', models.TextField(default='[]', help_text='流转条件表达式，根据表达式中的条件来确定流转的下个状态，格式为[{\"expression\":\"{days} > 3 and {days}<10\", \"target_state_id\":11}] 其中{}用于填充工单的字段key,运算时会换算成实际的值，当符合条件下个状态将变为target_state_id中的值,表达式只支持简单的运算或datetime/time运算.loonflow会以首次匹配成功的条件为准，所以多个条件不要有冲突', verbose_name='条件表达式')),\n                ('attribute_type', models.CharField(choices=[(0, '草稿'), (1, '待审'), (2, '驳回'), (3, '撤销'), (4, '结束'), (5, '已关闭')], default=0, max_length=1, verbose_name='属性类型')),\n                ('alert_enable', models.BooleanField(default=False, verbose_name='点击弹窗提示')),\n                ('alert_text', models.CharField(blank=True, default='', max_length=100, verbose_name='弹窗内容')),\n                ('dest_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dest_state', to='workflows.State', verbose_name='目的状态')),\n                ('source_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='source_state', to='workflows.State', verbose_name='源状态')),\n                ('workflow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflows.Workflow', verbose_name='工作流')),\n            ],\n            options={\n                'verbose_name': '工作流流转',\n                'verbose_name_plural': '工作流流转',\n            },\n        ),\n        migrations.AddField(\n            model_name='state',\n            name='workflow',\n            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflows.Workflow', verbose_name='工作流'),\n        ),\n        migrations.AddField(\n            model_name='customfield',\n            name='workflow',\n            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflows.Workflow', verbose_name='工作流'),\n        ),\n    ]\n"
  },
  {
    "path": "backend/workflows/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "backend/workflows/models.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom django.db import models\nfrom common.models import BaseModel\nfrom systems.models import *\n\n\nclass WorkflowType(BaseModel):\n    name = models.CharField('名称', max_length=50)\n    status = models.BooleanField(default=True)\n    code = models.CharField(max_length=32, unique=True, verbose_name='代码')\n    order_id = models.IntegerField('状态顺序', default=1)\n\n    def __str__(self):\n        return self.name\n\n    class Meta:\n        ordering = ['order_id']\n        verbose_name = '工作流类型'\n        verbose_name_plural = verbose_name\n\n\nclass Workflow(BaseModel):\n    \"\"\"\n    工作流\n    \"\"\"\n    name = models.CharField('名称', max_length=50)\n    key = models.CharField('流程标识key', blank=True, max_length=168)\n    ticket_sn_prefix = models.CharField('工单流水号前缀', default='xxoo', max_length=20)\n    status = models.BooleanField(default=True)\n    type = models.ForeignKey(WorkflowType, on_delete=models.CASCADE, verbose_name='工作流类型')\n    roles = models.ManyToManyField(Role, verbose_name='关联角色', blank=True, related_name=\"workflow_set\", related_query_name=\"workflow\")\n    view_permission_check = models.BooleanField('查看权限校验', default=True, help_text='开启后，只允许工单的关联人(创建人、曾经的处理人)有权限查看工单')\n    limit_expression = models.TextField('限制表达式', default='{}', blank=True,\n                                        help_text='限制周期({\"period\":24} 24小时), 限制次数({\"count\":1}在限制周期内只允许提交1次), 限制级别({\"level\":1} 针对(1单个用户 2全局)限制周期限制次数,默认特定用户);允许特定人员提交({\"allow_persons\":\"zhangsan,lisi\"}只允许张三提交工单,{\"allow_depts\":\"1,2\"}只允许部门id为1和2的用户提交工单，{\"allow_roles\":\"1,2\"}只允许角色id为1和2的用户提交工单)')\n    display_form_str = models.TextField('展现表单字段', default='[]', blank=True,\n                                        help_text='默认\"[]\"，用于用户只有对应工单查看权限时显示哪些字段,field_key的list的json,如[\"days\",\"sn\"],内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称)，state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称')\n    title_template = models.CharField('标题模板', max_length=50, default='你有一个待办工单:{title}', null=True, blank=True,\n                                      help_text='工单字段的值可以作为参数写到模板中，格式如：你有一个待办工单:{title}')\n\n    def __str__(self):\n        return self.name\n\n    class Meta:\n        verbose_name = '工作流'\n        verbose_name_plural = verbose_name\n\n\nfield_type = {\n    1: '字符串',\n    2: '整型',\n    3: '浮点型',\n    4: '布尔',\n    5: '日期',\n    6: '日期时间',\n    7: '范围日期',\n    8: '文本域',\n    9: '单选框',\n    10: '下拉列表',\n    11: '用户名',\n    12: '多选框',\n    13: '多选下拉',\n    14: '多选用户名',\n}\n\n\nclass CustomField(BaseModel):\n    \"\"\"自定义字段, 设定某个工作流有哪些自定义字段\"\"\"\n    workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='工作流')\n    field_attribute = models.BooleanField('字段是否内置', default=False)\n    field_type = models.CharField(max_length=1, choices=tuple(field_type.items()), default=0, verbose_name='字段类型')\n    field_key = models.CharField('字段标识', max_length=50, help_text='字段类型请尽量特殊，避免与系统中关键字冲突')\n    field_name = models.CharField('字段名称', max_length=50)\n    # 内置 field 的 order_id 不要超过10\n    order_id = models.IntegerField('排序', default=0)\n    default_value = models.CharField('默认值', null=True, blank=True, max_length=100, help_text='前端展示时，可以将此内容作为表单中的该字段的默认值')\n    field_template = models.TextField('文本域模板', default='', blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder')\n    boolean_field_display = models.CharField('布尔类型显示名', max_length=100, default='{}', blank=True,\n                                             help_text='当为布尔类型时候，可以支持自定义显示形式。{\"1\":\"是\",\"0\":\"否\"}或{\"1\":\"需要\",\"0\":\"不需要\"}，注意数字也需要引号')\n    field_choice = models.CharField('radio、checkbox、select的选项', max_length=255, default='{}', blank=True,\n                                    help_text='radio,checkbox,select,multiselect类型可供选择的选项，格式为json如:{\"1\":\"中国\", \"2\":\"美国\"},注意数字也需要引号')\n    label = models.CharField('标签', max_length=100, blank=True, default='{}',\n                             help_text='自定义标签，json格式，调用方可根据标签自行处理特殊场景逻辑，loonflow只保存文本内容')\n\n    def __str__(self):\n        return self.field_name\n\n    class Meta:\n        ordering = ['order_id']\n        verbose_name = '工作流自定义字段'\n        verbose_name_plural = verbose_name\n\n\n# 0.普通类型 1.初始状态(用于新建工单时,获取对应的字段必填及transition信息) 2.结束状态(此状态下的工单不得再处理，即没有对应的transition)\nstate_type = {\n    0: '普通状态',\n    1: '初始状态',\n    2: '结束状态',\n}\n\nparticipant_type = {\n    'none': '无处理人',\n    'user': '个人',\n    'group': '部门',\n    'role': '角色',\n}\n\n\nclass State(BaseModel):\n    \"\"\"\n    状态记录, 变量支持通过脚本获取\n    \"\"\"\n    name = models.CharField('名称', max_length=50)\n    workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='工作流')\n    is_hidden = models.BooleanField('是否隐藏', default=False, help_text='设置为True时,获取工单步骤api中不显示此状态(当前处于此状态时除外)')\n    order_id = models.IntegerField('状态顺序', default=1)\n    state_type = models.CharField(max_length=1, choices=tuple(state_type.items()), default=0, verbose_name='状态类型')\n    enable_retreat = models.BooleanField('允许撤回', default=False, help_text='开启后允许工单创建人在此状态直接撤回工单到初始状态')\n    participant_type = models.CharField(max_length=5, choices=tuple(participant_type.items()), default='none', verbose_name='参与者类型')\n    user_participant = models.ManyToManyField(User, blank=True, verbose_name='参与用户')\n    group_participant = models.ManyToManyField(Group, blank=True, verbose_name='参与组')\n    role_participant = models.ManyToManyField(Role, blank=True, verbose_name='参与角色')\n    fields = models.ManyToManyField(CustomField, blank=True, verbose_name='可编辑字段')\n\n    def __str__(self):\n        return self.name\n\n    class Meta:\n        ordering = ['order_id']\n        verbose_name = '工作流状态'\n        verbose_name_plural = verbose_name\n\n\ntransition_name = {\n    0: '保存',\n    1: '转交下一步',\n    2: '驳回',\n    3: '撤销',\n    4: '关闭',\n}\n\ntransition_type = {\n    0: '常规流转',\n    1: '定时器流转',\n}\n\nattribute_type = {\n    0: '草稿',\n    1: '待审',\n    2: '驳回',\n    3: '撤销',\n    4: '结束',\n    5: '已关闭',\n}\n\n\nclass Transition(BaseModel):\n    \"\"\"\n    工作流流转，定时器，条件(允许跳过)， 条件流转与定时器不可同时存在\n    \"\"\"\n    name = models.CharField('名称类型', max_length=1, choices=tuple(transition_name.items()), default=1)\n    workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='工作流')\n    transition_type = models.CharField('流转类型', max_length=1, choices=tuple(transition_type.items()), default=0)\n    timer = models.IntegerField('定时器(单位秒)', default=0, help_text='流转类型设置为定时器流转时生效,单位秒。处于源状态X秒后如果状态都没有过变化则自动流转到目标状态')\n    source_state = models.ForeignKey(State, null=True, blank=True, on_delete=models.SET_NULL, related_name=\"source_state\", verbose_name='源状态')\n    dest_state = models.ForeignKey(State, null=True, blank=True, on_delete=models.SET_NULL, related_name=\"dest_state\", verbose_name='目的状态')\n    condition_expression = models.TextField('条件表达式', default='[]',\n                                            help_text='流转条件表达式，根据表达式中的条件来确定流转的下个状态，格式为[{\"expression\":\"{days} > 3 and {days}<10\", \"target_state_id\":11}] 其中{}用于填充工单的字段key,运算时会换算成实际的值，当符合条件下个状态将变为target_state_id中的值,表达式只支持简单的运算或datetime/time运算.loonflow会以首次匹配成功的条件为准，所以多个条件不要有冲突')\n    attribute_type = models.CharField(max_length=1, choices=tuple(attribute_type.items()), default=0, verbose_name='属性类型')\n    alert_enable = models.BooleanField('点击弹窗提示', default=False)\n    alert_text = models.CharField('弹窗内容', max_length=100, default='', blank=True)\n\n    def __str__(self):\n        return self.name\n\n    class Meta:\n        verbose_name = '工作流流转'\n        verbose_name_plural = verbose_name\n\n\n\"\"\"\nhttps://github.com/GoldSubmarine/workflow-bpmn-modeler\nhttps://github.com/miyuesc/bpmn-process-designer\nhttps://juejin.cn/post/6844904069736169480\n\"\"\"\n\n\nclass WorkflowBpmn(BaseModel):\n    workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='工作流')\n    xml = models.TextField('xml数据', blank=True)\n    svg = models.TextField('svg数据', blank=True)\n\n    class Meta:\n        verbose_name = '工作流bpmn'\n        verbose_name_plural = verbose_name\n"
  },
  {
    "path": "backend/workflows/serializers.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom workflows.models import *\nfrom rest_framework import serializers\n\n\nclass WorkflowReadSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Workflow\n        fields = '__all__'\n        depth = 1\n\n\nclass WorkflowSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Workflow\n        fields = '__all__'\n\n    def create(self, validated_data):\n        roles = validated_data['roles']\n        del validated_data['roles']\n        obj = Workflow.objects.create(**validated_data)\n        obj.roles.set(roles)\n        obj.save()\n\n        # 建立初始和结束状态\n        State.objects.create(name=\"开始\", order_id=1, state_type=1, is_hidden=True, participant_type=0, workflow=obj)\n        State.objects.create(name=\"关闭\", order_id=99, state_type=2, is_hidden=True, participant_type=0, workflow=obj)\n\n        # 建立内置字段\n        CustomField.objects.create(field_name=\"申请人\", order_id=1, field_attribute=True, field_type=1,\n                                   field_key=\"create_user\", workflow=obj)\n        CustomField.objects.create(field_name=\"申请时间\", order_id=2, field_attribute=True, field_type=6,\n                                   field_key=\"create_time\", workflow=obj)\n        CustomField.objects.create(field_name=\"部门\", order_id=3, field_attribute=True, field_type=1, field_key=\"group\",\n                                   workflow=obj)\n        CustomField.objects.create(field_name=\"工号\", order_id=4, field_attribute=True, field_type=1, field_key=\"id\",\n                                   workflow=obj)\n\n        return obj\n\n\nclass WorkflowTypeSerializer(serializers.ModelSerializer):\n    workflow_list = serializers.SerializerMethodField()\n    # workflow_set = WorkflowSerializer(many=True, read_only=True,)\n\n    def get_workflow_list(self, instance):\n        a = instance.workflow_set.get_queryset().filter(status=True)\n        return a.values()\n\n    class Meta:\n        model = WorkflowType\n        fields = '__all__'\n\n\nclass StateReadSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = State\n        fields = '__all__'\n        depth = 1\n\n\nclass StateSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = State\n        fields = '__all__'\n\n\nclass TransitionReadSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Transition\n        fields = '__all__'\n        depth = 2\n\n\nclass TransitionSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Transition\n        fields = '__all__'\n\n\nclass CustomFieldSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = CustomField\n        fields = '__all__'\n\n\nclass WorkflowBpmnSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = WorkflowBpmn\n        fields = '__all__'\n"
  },
  {
    "path": "backend/workflows/urls.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\n\nfrom django.conf.urls import url, include\nfrom rest_framework import routers\nfrom workflows.views import WorkflowTypeViewSet, WorkflowViewSet, StateViewSet, TransitionViewSet, CustomFieldViewSet, WorkflowBpmnViewSet\n\nrouter = routers.DefaultRouter()\n\nrouter.register(r'workflowtype', WorkflowTypeViewSet)\nrouter.register(r'workflow', WorkflowViewSet)\nrouter.register('state', StateViewSet)\nrouter.register(r'transition', TransitionViewSet)\nrouter.register(r'customfield', CustomFieldViewSet)\nrouter.register(r'workflowbpmn', WorkflowBpmnViewSet)\n\n\nurlpatterns = [\n]\n\nurlpatterns += router.urls\n"
  },
  {
    "path": "backend/workflows/views.py",
    "content": "# -*- coding: utf-8 -*-\n# author: itimor\n\nfrom itertools import chain\nfrom workflows.serializers import *\nfrom common.views import ModelViewSet, FKModelViewSet, JsonResponse, BulkModelMixin\n\n\nclass WorkflowTypeViewSet(BulkModelMixin):\n    queryset = WorkflowType.objects.all()\n    serializer_class = WorkflowTypeSerializer\n    search_fields = ['name']\n    filter_fields = ['name', 'code', 'status']\n\n\nclass WorkflowViewSet(BulkModelMixin):\n    queryset = Workflow.objects.all()\n    serializer_class = WorkflowSerializer\n    search_fields = ['name']\n    filter_fields = ['name', 'id', 'type']\n\n    def get_serializer_class(self):\n        if self.action in ['list', 'retrieve'] or self.resultData:\n            return WorkflowReadSerializer\n        return WorkflowSerializer\n\n    # def get_queryset(self):\n    #     try:\n    #         user = User.objects.get(username=self.request.user)\n    #         if user.is_admin:\n    #             return Workflow.objects.all()\n    #         else:\n    #             user_roles = user.roles.all()\n    #             group_roles = user.group.roles.all()\n    #             all_roles = sorted(chain(user_roles, group_roles), key=lambda t: t.id, reverse=True)\n    #             return Workflow.objects.filter(roles__in=all_roles).distinct()\n    #     except Exception as e:\n    #         print(e)\n    #         return Workflow.objects.all()\n\n\nclass StateViewSet(BulkModelMixin):\n    queryset = State.objects.all()\n    serializer_class = StateSerializer\n    search_fields = ['name']\n    filter_fields = ['workflow', 'is_hidden']\n    ordering_fields = ['state_type', 'order_id']\n\n\nclass TransitionViewSet(BulkModelMixin):\n    queryset = Transition.objects.all()\n    serializer_class = TransitionSerializer\n    search_fields = ['name']\n    filter_fields = ['workflow', 'transition_type', 'source_state', 'dest_state']\n\n    def get_serializer_class(self):\n        if self.action in ['list', 'retrieve'] or self.resultData:\n            return TransitionReadSerializer\n        return TransitionSerializer\n\n\nclass CustomFieldViewSet(BulkModelMixin):\n    queryset = CustomField.objects.all()\n    serializer_class = CustomFieldSerializer\n    search_fields = ['field_name']\n    filter_fields = ['workflow', 'field_type', 'field_attribute']\n    ordering_fields = ['field_type', 'order_id']\n\n\nclass WorkflowBpmnViewSet(BulkModelMixin):\n    queryset = WorkflowBpmn.objects.all()\n    serializer_class = WorkflowBpmnSerializer\n"
  },
  {
    "path": "frontend/.editorconfig",
    "content": "# https://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.md]\ninsert_final_newline = false\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": "frontend/.eslintignore",
    "content": "build/*.js\nsrc/assets\npublic\ndist\n"
  },
  {
    "path": "frontend/.eslintrc.js",
    "content": "module.exports = {\n  root: true,\n  parserOptions: {\n    parser: 'babel-eslint',\n    sourceType: 'module'\n  },\n  env: {\n    browser: true,\n    node: true,\n    es6: true,\n  },\n  extends: ['plugin:vue/recommended', 'eslint:recommended'],\n\n  // add your custom rules here\n  //it is base on https://github.com/vuejs/eslint-config-vue\n  rules: {\n    \"vue/max-attributes-per-line\": [2, {\n      \"singleline\": 10,\n      \"multiline\": {\n        \"max\": 1,\n        \"allowFirstLine\": false\n      }\n    }],\n    \"vue/singleline-html-element-content-newline\": \"off\",\n    \"vue/multiline-html-element-content-newline\":\"off\",\n    \"vue/name-property-casing\": [\"error\", \"PascalCase\"],\n    \"vue/no-v-html\": \"off\",\n    'accessor-pairs': 2,\n    'arrow-spacing': [2, {\n      'before': true,\n      'after': true\n    }],\n    'block-spacing': [2, 'always'],\n    'brace-style': [2, '1tbs', {\n      'allowSingleLine': true\n    }],\n    'camelcase': [0, {\n      'properties': 'always'\n    }],\n    'comma-dangle': [2, 'never'],\n    'comma-spacing': [2, {\n      'before': false,\n      'after': true\n    }],\n    'comma-style': [2, 'last'],\n    'constructor-super': 2,\n    'curly': [2, 'multi-line'],\n    'dot-location': [2, 'property'],\n    'eol-last': 2,\n    'eqeqeq': [\"error\", \"always\", {\"null\": \"ignore\"}],\n    'generator-star-spacing': [2, {\n      'before': true,\n      'after': true\n    }],\n    'handle-callback-err': [2, '^(err|error)$'],\n    'indent': [2, 2, {\n      'SwitchCase': 1\n    }],\n    'jsx-quotes': [2, 'prefer-single'],\n    'key-spacing': [2, {\n      'beforeColon': false,\n      'afterColon': true\n    }],\n    'keyword-spacing': [2, {\n      'before': true,\n      'after': true\n    }],\n    'new-cap': [2, {\n      'newIsCap': true,\n      'capIsNew': false\n    }],\n    'new-parens': 2,\n    'no-array-constructor': 2,\n    'no-caller': 2,\n    'no-console': 'off',\n    'no-class-assign': 2,\n    'no-cond-assign': 2,\n    'no-const-assign': 2,\n    'no-control-regex': 0,\n    'no-delete-var': 2,\n    'no-dupe-args': 2,\n    'no-dupe-class-members': 2,\n    'no-dupe-keys': 2,\n    'no-duplicate-case': 2,\n    'no-empty-character-class': 2,\n    'no-empty-pattern': 2,\n    'no-eval': 2,\n    'no-ex-assign': 2,\n    'no-extend-native': 2,\n    'no-extra-bind': 2,\n    'no-extra-boolean-cast': 2,\n    'no-extra-parens': [2, 'functions'],\n    'no-fallthrough': 2,\n    'no-floating-decimal': 2,\n    'no-func-assign': 2,\n    'no-implied-eval': 2,\n    'no-inner-declarations': [2, 'functions'],\n    'no-invalid-regexp': 2,\n    'no-irregular-whitespace': 2,\n    'no-iterator': 2,\n    'no-label-var': 2,\n    'no-labels': [2, {\n      'allowLoop': false,\n      'allowSwitch': false\n    }],\n    'no-lone-blocks': 2,\n    'no-mixed-spaces-and-tabs': 2,\n    'no-multi-spaces': 2,\n    'no-multi-str': 2,\n    'no-multiple-empty-lines': [2, {\n      'max': 1\n    }],\n    'no-native-reassign': 2,\n    'no-negated-in-lhs': 2,\n    'no-new-object': 2,\n    'no-new-require': 2,\n    'no-new-symbol': 2,\n    'no-new-wrappers': 2,\n    'no-obj-calls': 2,\n    'no-octal': 2,\n    'no-octal-escape': 2,\n    'no-path-concat': 2,\n    'no-proto': 2,\n    'no-redeclare': 2,\n    'no-regex-spaces': 2,\n    'no-return-assign': [2, 'except-parens'],\n    'no-self-assign': 2,\n    'no-self-compare': 2,\n    'no-sequences': 2,\n    'no-shadow-restricted-names': 2,\n    'no-spaced-func': 2,\n    'no-sparse-arrays': 2,\n    'no-this-before-super': 2,\n    'no-throw-literal': 2,\n    'no-trailing-spaces': 2,\n    'no-undef': 2,\n    'no-undef-init': 2,\n    'no-unexpected-multiline': 2,\n    'no-unmodified-loop-condition': 2,\n    'no-unneeded-ternary': [2, {\n      'defaultAssignment': false\n    }],\n    'no-unreachable': 2,\n    'no-unsafe-finally': 2,\n    'no-unused-vars': [2, {\n      'vars': 'all',\n      'args': 'none'\n    }],\n    'no-useless-call': 2,\n    'no-useless-computed-key': 2,\n    'no-useless-constructor': 2,\n    'no-useless-escape': 0,\n    'no-whitespace-before-property': 2,\n    'no-with': 2,\n    'one-var': [2, {\n      'initialized': 'never'\n    }],\n    'operator-linebreak': [2, 'after', {\n      'overrides': {\n        '?': 'before',\n        ':': 'before'\n      }\n    }],\n    'padded-blocks': [2, 'never'],\n    'quotes': [2, 'single', {\n      'avoidEscape': true,\n      'allowTemplateLiterals': true\n    }],\n    'semi': [2, 'never'],\n    'semi-spacing': [2, {\n      'before': false,\n      'after': true\n    }],\n    'space-before-blocks': [2, 'always'],\n    'space-before-function-paren': [2, 'never'],\n    'space-in-parens': [2, 'never'],\n    'space-infix-ops': 2,\n    'space-unary-ops': [2, {\n      'words': true,\n      'nonwords': false\n    }],\n    'spaced-comment': [2, 'always', {\n      'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']\n    }],\n    'template-curly-spacing': [2, 'never'],\n    'use-isnan': 2,\n    'valid-typeof': 2,\n    'wrap-iife': [2, 'any'],\n    'yield-star-spacing': [2, 'both'],\n    'yoda': [2, 'never'],\n    'prefer-const': 2,\n    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,\n    'object-curly-spacing': [2, 'always', {\n      objectsInObjects: false\n    }],\n    'array-bracket-spacing': [2, 'never']\n  }\n}\n"
  },
  {
    "path": "frontend/.travis.yml",
    "content": "language: node_js\nnode_js: 10\nscript: npm run test\nnotifications:\n  email: false\n"
  },
  {
    "path": "frontend/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017-present PanJiaChen\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "frontend/babel.config.js",
    "content": "module.exports = {\n  presets: [\n    '@vue/app'\n  ]\n}\n"
  },
  {
    "path": "frontend/build/index.js",
    "content": "const { run } = require('runjs')\nconst chalk = require('chalk')\nconst config = require('../vue.config.js')\nconst rawArgv = process.argv.slice(2)\nconst args = rawArgv.join(' ')\n\nif (process.env.npm_config_preview || rawArgv.includes('--preview')) {\n  const report = rawArgv.includes('--report')\n\n  run(`vue-cli-service build ${args}`)\n\n  const port = 9526\n  const publicPath = config.publicPath\n\n  var connect = require('connect')\n  var serveStatic = require('serve-static')\n  const app = connect()\n\n  app.use(\n    publicPath,\n    serveStatic('./dist', {\n      index: ['index.html', '/']\n    })\n  )\n\n  app.listen(port, function () {\n    console.log(chalk.green(`> Preview at  http://localhost:${port}${publicPath}`))\n    if (report) {\n      console.log(chalk.green(`> Report at  http://localhost:${port}${publicPath}report.html`))\n    }\n\n  })\n} else {\n  run(`vue-cli-service build ${args}`)\n}\n"
  },
  {
    "path": "frontend/jest.config.js",
    "content": "module.exports = {\n  moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],\n  transform: {\n    '^.+\\\\.vue$': 'vue-jest',\n    '.+\\\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':\n      'jest-transform-stub',\n    '^.+\\\\.jsx?$': 'babel-jest'\n  },\n  moduleNameMapper: {\n    '^@/(.*)$': '<rootDir>/src/$1'\n  },\n  snapshotSerializers: ['jest-serializer-vue'],\n  testMatch: [\n    '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'\n  ],\n  collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'],\n  coverageDirectory: '<rootDir>/tests/unit/coverage',\n  // 'collectCoverage': true,\n  'coverageReporters': [\n    'lcov',\n    'text-summary'\n  ],\n  testURL: 'http://localhost/'\n}\n"
  },
  {
    "path": "frontend/jsconfig.json",
    "content": "{ \n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"paths\": {\n        \"@/*\": [\"src/*\"]\n    }\n  },\n  \"exclude\": [\"node_modules\", \"dist\"]\n}"
  },
  {
    "path": "frontend/package.json",
    "content": "{\n  \"name\": \"vue-element-admin\",\n  \"version\": \"4.0.0\",\n  \"description\": \"A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features\",\n  \"author\": \"Pan <panfree23@gmail.com>\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"dev\": \"vue-cli-service serve\",\n    \"build\": \"vue-cli-service build\",\n    \"lint\": \"eslint --ext .js,.vue src\",\n    \"svgo\": \"svgo -f src/icons/svg --config=src/icons/svgo.yml\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"lint-staged\"\n    }\n  },\n  \"lint-staged\": {\n    \"src/**/*.{js,vue}\": [\n      \"eslint --fix\",\n      \"git add\"\n    ]\n  },\n  \"keywords\": [\n    \"vue\",\n    \"admin\",\n    \"dashboard\",\n    \"element-ui\",\n    \"boilerplate\",\n    \"admin-template\",\n    \"management-system\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/PanJiaChen/vue-element-admin.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/PanJiaChen/vue-element-admin/issues\"\n  },\n  \"dependencies\": {\n    \"axios\": \"^0.19.2\",\n    \"csshake\": \"^1.5.3\",\n    \"element-ui\": \"^2.12.0\",\n    \"fibers\": \"^5.0.0\",\n    \"path-to-regexp\": \"^6.1.0\",\n    \"vue\": \"^2.6.11\",\n    \"vue-bpmn-modeler\": \"^1.2.0\",\n    \"vue-i18n\": \"^8.15.0\",\n    \"vue-markdown\": \"^2.2.4\",\n    \"vue-route\": \"^1.5.1\",\n    \"vuex\": \"^3.1.1\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"7.0.0\",\n    \"@babel/register\": \"7.0.0\",\n    \"@vue/cli-plugin-babel\": \"3.5.3\",\n    \"@vue/cli-plugin-eslint\": \"^3.5.1\",\n    \"@vue/cli-plugin-unit-jest\": \"3.5.3\",\n    \"@vue/cli-service\": \"3.5.3\",\n    \"babel-core\": \"^6.26.3\",\n    \"babel-eslint\": \"^10.1.0\",\n    \"chalk\": \"^2.4.2\",\n    \"clipboard\": \"^2.0.4\",\n    \"echarts\": \"^4.7.0\",\n    \"eslint\": \"^6.8.0\",\n    \"eslint-plugin-vue\": \"^6.2.2\",\n    \"fuse.js\": \"^3.6.1\",\n    \"html-webpack-plugin\": \"^3.2.0\",\n    \"js-cookie\": \"^2.2.1\",\n    \"node-sass\": \"^4.14.0\",\n    \"normalize.css\": \"^8.0.1\",\n    \"nprogress\": \"^0.2.0\",\n    \"sass-loader\": \"^8.0.2\",\n    \"screenfull\": \"^5.0.2\",\n    \"script-ext-html-webpack-plugin\": \"^2.1.4\",\n    \"script-loader\": \"^0.7.2\",\n    \"svg-sprite-loader\": \"^4.2.5\",\n    \"svgo\": \"^1.3.2\",\n    \"vue-count-to\": \"^1.0.13\",\n    \"vue-router\": \"^3.1.6\",\n    \"vue-template-compiler\": \"^2.6.11\",\n    \"webpack\": \"^4.43.0\",\n    \"webpack-cli\": \"^3.3.11\",\n    \"webpack-dev-server\": \"^3.10.3\"\n  },\n  \"engines\": {\n    \"node\": \">=8.9\",\n    \"npm\": \">= 3.0.0\"\n  },\n  \"browserslist\": [\n    \"> 1%\",\n    \"last 2 versions\",\n    \"not ie <= 8\"\n  ]\n}\n"
  },
  {
    "path": "frontend/plop-templates/component/index.hbs",
    "content": "{{#if template}}\n<template>\n  <div />\n</template>\n{{/if}}\n\n{{#if script}}\n<script>\nexport default {\n  name: '{{ properCase name }}',\n  props: {},\n  data() {\n    return {}\n  },\n  created() {},\n  mounted() {},\n  methods: {}\n}\n</script>\n{{/if}}\n\n{{#if style}}\n<style lang=\"scss\" scoped>\n\n</style>\n{{/if}}\n"
  },
  {
    "path": "frontend/plop-templates/component/prompt.js",
    "content": "const { notEmpty } = require('../utils.js')\n\nmodule.exports = {\n  description: 'generate vue component',\n  prompts: [{\n    type: 'input',\n    name: 'name',\n    message: 'component name please',\n    validate: notEmpty('name')\n  },\n  {\n    type: 'checkbox',\n    name: 'blocks',\n    message: 'Blocks:',\n    choices: [{\n      name: '<template>',\n      value: 'template',\n      checked: true\n    },\n    {\n      name: '<script>',\n      value: 'script',\n      checked: true\n    },\n    {\n      name: 'style',\n      value: 'style',\n      checked: true\n    }\n    ],\n    validate(value) {\n      if (value.indexOf('script') === -1 && value.indexOf('template') === -1) {\n        return 'Components require at least a <script> or <template> tag.'\n      }\n      return true\n    }\n  }\n  ],\n  actions: data => {\n    const name = '{{properCase name}}'\n    const actions = [{\n      type: 'add',\n      path: `src/components/${name}/index.vue`,\n      templateFile: 'plop-templates/component/index.hbs',\n      data: {\n        name: name,\n        template: data.blocks.includes('template'),\n        script: data.blocks.includes('script'),\n        style: data.blocks.includes('style')\n      }\n    }]\n\n    return actions\n  }\n}\n"
  },
  {
    "path": "frontend/plop-templates/store/index.hbs",
    "content": "{{#if state}}\nconst state = {}\n{{/if}}\n\n{{#if mutations}}\nconst mutations = {}\n{{/if}}\n\n{{#if actions}}\nconst actions = {}\n{{/if}}\n\nexport default {\n  namespaced: true,\n  {{options}}\n}\n"
  },
  {
    "path": "frontend/plop-templates/store/prompt.js",
    "content": "const { notEmpty } = require('../utils.js')\n\nmodule.exports = {\n  description: 'generate store',\n  prompts: [{\n    type: 'input',\n    name: 'name',\n    message: 'store name please',\n    validate: notEmpty('name')\n  },\n  {\n    type: 'checkbox',\n    name: 'blocks',\n    message: 'Blocks:',\n    choices: [{\n      name: 'state',\n      value: 'state',\n      checked: true\n    },\n    {\n      name: 'mutations',\n      value: 'mutations',\n      checked: true\n    },\n    {\n      name: 'actions',\n      value: 'actions',\n      checked: true\n    }\n    ],\n    validate(value) {\n      if (!value.includes('state') || !value.includes('mutations')) {\n        return 'store require at least state and mutations'\n      }\n      return true\n    }\n  }\n  ],\n  actions(data) {\n    const name = '{{name}}'\n    const { blocks } = data\n    const options = ['state', 'mutations']\n    const joinFlag = `,\n  `\n    if (blocks.length === 3) {\n      options.push('actions')\n    }\n\n    const actions = [{\n      type: 'add',\n      path: `src/store/modules/${name}.js`,\n      templateFile: 'plop-templates/store/index.hbs',\n      data: {\n        options: options.join(joinFlag),\n        state: blocks.includes('state'),\n        mutations: blocks.includes('mutations'),\n        actions: blocks.includes('actions')\n      }\n    }]\n    return actions\n  }\n}\n"
  },
  {
    "path": "frontend/plop-templates/utils.js",
    "content": "exports.notEmpty = name => {\n  return v => {\n    if (!v || v.trim === '') {\n      return `${name} is required`\n    } else {\n      return true\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/plop-templates/view/index.hbs",
    "content": "{{#if template}}\n<template>\n  <div />\n</template>\n{{/if}}\n\n{{#if script}}\n<script>\nexport default {\n  name: '{{ properCase name }}',\n  props: {},\n  data() {\n    return {}\n  },\n  created() {},\n  mounted() {},\n  methods: {}\n}\n</script>\n{{/if}}\n\n{{#if style}}\n<style lang=\"scss\" scoped>\n\n</style>\n{{/if}}\n"
  },
  {
    "path": "frontend/plop-templates/view/prompt.js",
    "content": "const { notEmpty } = require('../utils.js')\n\nmodule.exports = {\n  description: 'generate a view',\n  prompts: [{\n    type: 'input',\n    name: 'name',\n    message: 'view name please',\n    validate: notEmpty('name')\n  },\n  {\n    type: 'checkbox',\n    name: 'blocks',\n    message: 'Blocks:',\n    choices: [{\n      name: '<template>',\n      value: 'template',\n      checked: true\n    },\n    {\n      name: '<script>',\n      value: 'script',\n      checked: true\n    },\n    {\n      name: 'style',\n      value: 'style',\n      checked: true\n    }\n    ],\n    validate(value) {\n      if (value.indexOf('script') === -1 && value.indexOf('template') === -1) {\n        return 'View require at least a <script> or <template> tag.'\n      }\n      return true\n    }\n  }\n  ],\n  actions: data => {\n    const name = '{{name}}'\n    const actions = [{\n      type: 'add',\n      path: `src/views/${name}/index.vue`,\n      templateFile: 'plop-templates/view/index.hbs',\n      data: {\n        name: name,\n        template: data.blocks.includes('template'),\n        script: data.blocks.includes('script'),\n        style: data.blocks.includes('style')\n      }\n    }]\n\n    return actions\n  }\n}\n"
  },
  {
    "path": "frontend/plopfile.js",
    "content": "const viewGenerator = require('./plop-templates/view/prompt')\nconst componentGenerator = require('./plop-templates/component/prompt')\nconst storeGenerator = require('./plop-templates/store/prompt.js')\n\nmodule.exports = function(plop) {\n  plop.setGenerator('view', viewGenerator)\n  plop.setGenerator('component', componentGenerator)\n  plop.setGenerator('store', storeGenerator)\n}\n"
  },
  {
    "path": "frontend/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    autoprefixer: {}\n  }\n}\n"
  },
  {
    "path": "frontend/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\">\n    <meta name=\"renderer\" content=\"webkit\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">\n    <link rel=\"icon\" href=\"http://softqual.co.il/wp-content/uploads/2017/06/cropped-favicon.png\">\n    <title><%= webpackConfig.name %></title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <!-- built files will be auto injected -->\n  </body>\n</html>\n"
  },
  {
    "path": "frontend/src/App.vue",
    "content": "<template>\n  <div id=\"app\">\n    <router-view />\n    <el-backtop :visibility-height=\"100\">\n      <el-tooltip placement=\"top\" content=\"ちょっと待って\">\n        <div class=\"shake shake-crazy\">\n          <svg-icon icon-class=\"rocket\" style=\"width: 40px; height: 40px;\" />\n        </div>\n      </el-tooltip>\n    </el-backtop>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: \"App\"\n};\n</script>\n"
  },
  {
    "path": "frontend/src/api/all.js",
    "content": "import Request from '@/api/common'\n\n// auth\nimport * as auths from '@/api/auths'\nexport const auth = auths\n\n// systems\nexport const user = new Request('/sys/user/')\nexport const group = new Request('/sys/group/')\nexport const role = new Request('/sys/role/')\nexport const menu = new Request('/sys/menu/')\nexport const perm = new Request('/sys/perm/')\n\n// tools\nexport const audit = new Request('/tool/audit/')\nexport const simple = new Request('/tool/simple/')\n\n// workflows\nexport const workflowtype = new Request('/workflow/workflowtype/')\nexport const workflow = new Request('/workflow/workflow/')\nexport const state = new Request('/workflow/state/')\nexport const transition = new Request('/workflow/transition/')\nexport const customfield = new Request('/workflow/customfield/')\n\n// tickets\nexport const ticket = new Request('/ticket/ticket/')\nexport const ticketflowlog = new Request('/ticket/ticketflowlog/')\nexport const ticketcustomfield = new Request('/ticket/ticketcustomfield/')\nexport const ticketuser = new Request('/ticket/ticketuser/')\n\n// notices\nexport const mail = new Request('/notice/mail/')\nexport const telegram = new Request('/notice/telegram/')\n"
  },
  {
    "path": "frontend/src/api/auths.js",
    "content": "import request from '@/utils/request'\n\nexport function login(data) {\n  return request({\n    url: '/sys/auth/jwt-token-auth/',\n    method: 'post',\n    data\n  })\n}\n\nexport function getInfo() {\n  return request({\n    url: '/sys/auth/getuserinfo/',\n    method: 'get'\n  })\n}\n\nexport function requestMenuButton(menucode) {\n  return request({\n    url: '/sys/auth/getmenubutons/',\n    method: 'get',\n    params: {menucode}\n  })\n}\n\nexport function changepwd(data) {\n  return request({\n    url: '/sys/auth/changepwd/',\n    method: 'post',\n    data\n  })\n}\n\nexport function getuser_by_group(data) {\n  return request({\n    url: '/sys/user/getuser_by_group/',\n    method: 'post',\n    data\n  })\n}\n\nexport function getuser_by_roles(data) {\n  return request({\n    url: '/sys/user/getuser_by_roles/',\n    method: 'post',\n    data\n  })\n}"
  },
  {
    "path": "frontend/src/api/common.js",
    "content": "import request from '@/utils/request'\n\nexport default class Request {\n  constructor(apiurl) {\n    this.apiurl = apiurl;\n  }\n\n  requestPost(data) {\n    return request({\n      url: this.apiurl,\n      method: 'post',\n      data\n    })\n  }\n\n  requestDelete(id) {\n    return request({\n      url: this.apiurl + id + '/',\n      method: 'delete'\n    })\n  }\n\n  requestPut(id, data) {\n    return request({\n      url: this.apiurl + id + '/',\n      method: 'put',\n      data\n    })\n  }\n\n  requestGet(query) {\n    return request({\n      url: this.apiurl,\n      method: 'get',\n      params: query\n    })\n  }\n\n  requestBulkPost(data) {\n    return request({\n      url: this.apiurl + 'bulk_create/',\n      method: 'post',\n      data\n    })\n  }\n\n  requestBulkPut(data) {\n    return request({\n      url: this.apiurl + 'bulk_update/',\n      method: 'put',\n      data\n    })\n  }\n\n  requestBulkDelete(data) {\n    return request({\n      url: this.apiurl + 'bulk_delete/',\n      method: 'delete',\n      data\n    })\n  }\n}"
  },
  {
    "path": "frontend/src/assets/custom-theme/index.css",
    "content": "@charset \"UTF-8\";.custom-theme .fade-in-linear-enter-active,.custom-theme .fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.custom-theme .fade-in-linear-enter,.custom-theme .fade-in-linear-leave,.custom-theme .fade-in-linear-leave-active{opacity:0}.custom-theme .el-fade-in-linear-enter-active,.custom-theme .el-fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.custom-theme .el-fade-in-linear-enter,.custom-theme .el-fade-in-linear-leave,.custom-theme .el-fade-in-linear-leave-active{opacity:0}.custom-theme .el-fade-in-enter-active,.custom-theme .el-fade-in-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.custom-theme .el-fade-in-enter,.custom-theme .el-fade-in-leave-active{opacity:0}.custom-theme .el-zoom-in-center-enter-active,.custom-theme .el-zoom-in-center-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.custom-theme .el-zoom-in-center-enter,.custom-theme .el-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.custom-theme .el-zoom-in-top-enter-active,.custom-theme .el-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:center top;transform-origin:center top}.custom-theme .el-zoom-in-top-enter,.custom-theme .el-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.custom-theme .el-zoom-in-bottom-enter-active,.custom-theme .el-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:center bottom;transform-origin:center bottom}.custom-theme .el-zoom-in-bottom-enter,.custom-theme .el-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.custom-theme .el-zoom-in-left-enter-active,.custom-theme .el-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1,1);transform:scale(1,1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:top left;transform-origin:top left}.custom-theme .el-zoom-in-left-enter,.custom-theme .el-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45,.45);transform:scale(.45,.45)}.custom-theme .collapse-transition{-webkit-transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out;transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out}.custom-theme .horizontal-collapse-transition{-webkit-transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out;transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out}.custom-theme .el-list-enter-active,.custom-theme .el-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.custom-theme .el-list-enter,.custom-theme .el-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.custom-theme .el-opacity-transition{-webkit-transition:opacity .3s cubic-bezier(.55,0,.1,1);transition:opacity .3s cubic-bezier(.55,0,.1,1)}@font-face{font-family:element-icons;src:url(fonts/element-icons.woff?t=1508751886602) format(\"woff\"),url(fonts/element-icons.ttf?t=1508751886602) format(\"truetype\");font-weight:400;font-style:normal}.custom-theme [class*=\" el-icon-\"],.custom-theme [class^=el-icon-]{font-family:element-icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.custom-theme .el-icon-upload:before{content:\"\\e60d\"}.custom-theme .el-icon-error:before{content:\"\\e62c\"}.custom-theme .el-icon-success:before{content:\"\\e62d\"}.custom-theme .el-icon-warning:before{content:\"\\e62e\"}.custom-theme .el-icon-sort-down:before{content:\"\\e630\"}.custom-theme .el-icon-sort-up:before{content:\"\\e631\"}.custom-theme .el-icon-arrow-left:before{content:\"\\e600\"}.custom-theme .el-icon-circle-plus:before{content:\"\\e601\"}.custom-theme .el-icon-circle-plus-outline:before{content:\"\\e602\"}.custom-theme .el-icon-arrow-down:before{content:\"\\e603\"}.custom-theme .el-icon-arrow-right:before{content:\"\\e604\"}.custom-theme .el-icon-arrow-up:before{content:\"\\e605\"}.custom-theme .el-icon-back:before{content:\"\\e606\"}.custom-theme .el-icon-circle-close:before{content:\"\\e607\"}.custom-theme .el-icon-date:before{content:\"\\e608\"}.custom-theme .el-icon-circle-close-outline:before{content:\"\\e609\"}.custom-theme .el-icon-caret-left:before{content:\"\\e60a\"}.custom-theme .el-icon-caret-bottom:before{content:\"\\e60b\"}.custom-theme .el-icon-caret-top:before{content:\"\\e60c\"}.custom-theme .el-icon-caret-right:before{content:\"\\e60e\"}.custom-theme .el-icon-close:before{content:\"\\e60f\"}.custom-theme .el-icon-d-arrow-left:before{content:\"\\e610\"}.custom-theme .el-icon-check:before{content:\"\\e611\"}.custom-theme .el-icon-delete:before{content:\"\\e612\"}.custom-theme .el-icon-d-arrow-right:before{content:\"\\e613\"}.custom-theme .el-icon-document:before{content:\"\\e614\"}.custom-theme .el-icon-d-caret:before{content:\"\\e615\"}.custom-theme .el-icon-edit-outline:before{content:\"\\e616\"}.custom-theme .el-icon-download:before{content:\"\\e617\"}.custom-theme .el-icon-goods:before{content:\"\\e618\"}.custom-theme .el-icon-search:before{content:\"\\e619\"}.custom-theme .el-icon-info:before{content:\"\\e61a\"}.custom-theme .el-icon-message:before{content:\"\\e61b\"}.custom-theme .el-icon-edit:before{content:\"\\e61c\"}.custom-theme .el-icon-location:before{content:\"\\e61d\"}.custom-theme .el-icon-loading:before{content:\"\\e61e\"}.custom-theme .el-icon-location-outline:before{content:\"\\e61f\"}.custom-theme .el-icon-menu:before{content:\"\\e620\"}.custom-theme .el-icon-minus:before{content:\"\\e621\"}.custom-theme .el-icon-bell:before{content:\"\\e622\"}.custom-theme .el-icon-mobile-phone:before{content:\"\\e624\"}.custom-theme .el-icon-news:before{content:\"\\e625\"}.custom-theme .el-icon-more:before{content:\"\\e646\"}.custom-theme .el-icon-more-outline:before{content:\"\\e626\"}.custom-theme .el-icon-phone:before{content:\"\\e627\"}.custom-theme .el-icon-phone-outline:before{content:\"\\e628\"}.custom-theme .el-icon-picture:before{content:\"\\e629\"}.custom-theme .el-icon-picture-outline:before{content:\"\\e62a\"}.custom-theme .el-icon-plus:before{content:\"\\e62b\"}.custom-theme .el-icon-printer:before{content:\"\\e62f\"}.custom-theme .el-icon-rank:before{content:\"\\e632\"}.custom-theme .el-icon-refresh:before{content:\"\\e633\"}.custom-theme .el-icon-question:before{content:\"\\e634\"}.custom-theme .el-icon-remove:before{content:\"\\e635\"}.custom-theme .el-icon-share:before{content:\"\\e636\"}.custom-theme .el-icon-star-on:before{content:\"\\e637\"}.custom-theme .el-icon-setting:before{content:\"\\e638\"}.custom-theme .el-icon-circle-check:before{content:\"\\e639\"}.custom-theme .el-icon-service:before{content:\"\\e63a\"}.custom-theme .el-icon-sold-out:before{content:\"\\e63b\"}.custom-theme .el-icon-remove-outline:before{content:\"\\e63c\"}.custom-theme .el-icon-star-off:before{content:\"\\e63d\"}.custom-theme .el-icon-circle-check-outline:before{content:\"\\e63e\"}.custom-theme .el-icon-tickets:before{content:\"\\e63f\"}.custom-theme .el-icon-sort:before{content:\"\\e640\"}.custom-theme .el-icon-zoom-in:before{content:\"\\e641\"}.custom-theme .el-icon-time:before{content:\"\\e642\"}.custom-theme .el-icon-view:before{content:\"\\e643\"}.custom-theme .el-icon-upload2:before{content:\"\\e644\"}.custom-theme .el-icon-zoom-out:before{content:\"\\e645\"}.custom-theme .el-icon-loading{-webkit-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}.custom-theme .el-icon--right{margin-left:5px}.custom-theme .el-icon--left{margin-right:5px}@-webkit-keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}}@keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}}.custom-theme .el-popper .popper__arrow,.custom-theme .el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.custom-theme .el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.custom-theme .el-popper .popper__arrow::after{content:\" \";border-width:6px}.custom-theme .el-popper[x-placement^=top]{margin-bottom:12px}.custom-theme .el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.custom-theme .el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.custom-theme .el-popper[x-placement^=bottom]{margin-top:12px}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.custom-theme .el-popper[x-placement^=right]{margin-left:12px}.custom-theme .el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.custom-theme .el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.custom-theme .el-popper[x-placement^=left]{margin-right:12px}.custom-theme .el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.custom-theme .el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.custom-theme .el-select-dropdown{position:absolute;z-index:1001;border:solid 1px #dfe4ed;border-radius:4px;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:5px 0}.custom-theme .el-select-dropdown.is-multiple .el-select-dropdown__item.selected{color:#262729;background-color:#fff}.custom-theme .el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover{background-color:#f5f7fa}.custom-theme .el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after{position:absolute;right:20px;font-family:element-icons;content:\"\\E611\";font-size:12px;font-weight:700;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.custom-theme .el-select-dropdown .el-scrollbar.is-empty .el-select-dropdown__list{padding:0}.custom-theme .el-select-dropdown .popper__arrow{-webkit-transform:translateX(-400%);transform:translateX(-400%)}.custom-theme .el-select-dropdown.is-arrow-fixed .popper__arrow{-webkit-transform:translateX(-200%);transform:translateX(-200%)}.custom-theme .el-select-dropdown__empty{padding:10px 0;margin:0;text-align:center;color:#999;font-size:14px}.custom-theme .el-select-dropdown__wrap{max-height:274px}.custom-theme .el-select-dropdown__list{list-style:none;padding:6px 0;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.custom-theme .el-input::-webkit-scrollbar{z-index:11;width:6px}.custom-theme .el-input::-webkit-scrollbar:horizontal{height:6px}.custom-theme .el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.custom-theme .el-input::-webkit-scrollbar-corner{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.custom-theme .el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.custom-theme .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input__inner:hover{border-color:#b4bccc}.custom-theme .el-input__inner:focus{outline:0;border-color:#262729}.custom-theme .el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.custom-theme .el-input__suffix-inner{pointer-events:all}.custom-theme .el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s}.custom-theme .el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.custom-theme .el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.custom-theme .el-input__validateIcon{pointer-events:none}.custom-theme .el-input.is-active .el-input__inner{outline:0;border-color:#262729}.custom-theme .el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__icon{cursor:not-allowed}.custom-theme .el-input--suffix .el-input__inner{padding-right:30px}.custom-theme .el-input--prefix .el-input__inner{padding-left:30px}.custom-theme .el-input--medium{font-size:14px}.custom-theme .el-input--medium .el-input__inner{height:36px}.custom-theme .el-input--medium .el-input__icon{line-height:36px}.custom-theme .el-input--small{font-size:13px}.custom-theme .el-input--small .el-input__inner{height:32px}.custom-theme .el-input--small .el-input__icon{line-height:32px}.custom-theme .el-input--mini{font-size:12px}.custom-theme .el-input--mini .el-input__inner{height:28px}.custom-theme .el-input--mini .el-input__icon{line-height:28px}.custom-theme .el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.custom-theme .el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.custom-theme .el-input-group__append,.custom-theme .el-input-group__prepend{background-color:#f5f7fa;color:#0a76a4;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.custom-theme .el-input-group__append:focus,.custom-theme .el-input-group__prepend:focus{outline:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-select,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-select{display:inline-block;margin:-20px}.custom-theme .el-input-group__append button.el-button,.custom-theme .el-input-group__append div.el-select .el-input__inner,.custom-theme .el-input-group__append div.el-select:hover .el-input__inner,.custom-theme .el-input-group__prepend button.el-button,.custom-theme .el-input-group__prepend div.el-select .el-input__inner,.custom-theme .el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-input,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-input{font-size:inherit}.custom-theme .el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-textarea{display:inline-block;width:100%;vertical-align:bottom}.custom-theme .el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:hover{border-color:#b4bccc}.custom-theme .el-textarea__inner:focus{outline:0;border-color:#262729}.custom-theme .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-tag{background-color:rgba(38,39,41,.1);display:inline-block;padding:0 10px;height:32px;line-height:30px;font-size:12px;color:#262729;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid rgba(38,39,41,.2);white-space:nowrap}.custom-theme .el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:18px;width:18px;line-height:18px;vertical-align:middle;top:-1px;right:-5px;color:#262729}.custom-theme .el-tag .el-icon-close::before{display:block}.custom-theme .el-tag .el-icon-close:hover{background-color:#262729;color:#fff}.custom-theme .el-tag--info{background-color:rgba(10,118,164,.1);border-color:rgba(10,118,164,.2);color:#0a76a4}.custom-theme .el-tag--info.is-hit{border-color:#0a76a4}.custom-theme .el-tag--info .el-tag__close{color:#0a76a4}.custom-theme .el-tag--info .el-tag__close:hover{background-color:#0a76a4;color:#fff}.custom-theme .el-tag--success{background-color:rgba(64,145,103,.1);border-color:rgba(64,145,103,.2);color:#409167}.custom-theme .el-tag--success.is-hit{border-color:#409167}.custom-theme .el-tag--success .el-tag__close{color:#409167}.custom-theme .el-tag--success .el-tag__close:hover{background-color:#409167;color:#fff}.custom-theme .el-tag--warning{background-color:rgba(157,164,8,.1);border-color:rgba(157,164,8,.2);color:#9da408}.custom-theme .el-tag--warning.is-hit{border-color:#9da408}.custom-theme .el-tag--warning .el-tag__close{color:#9da408}.custom-theme .el-tag--warning .el-tag__close:hover{background-color:#9da408;color:#fff}.custom-theme .el-tag--danger{background-color:rgba(179,69,14,.1);border-color:rgba(179,69,14,.2);color:#b3450e}.custom-theme .el-tag--danger.is-hit{border-color:#b3450e}.custom-theme .el-tag--danger .el-tag__close{color:#b3450e}.custom-theme .el-tag--danger .el-tag__close:hover{background-color:#b3450e;color:#fff}.custom-theme .el-tag--medium{height:28px;line-height:26px}.custom-theme .el-tag--medium .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-tag--small{height:24px;padding:0 8px;line-height:22px}.custom-theme .el-tag--small .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-tag--mini{height:20px;padding:0 5px;line-height:19px}.custom-theme .el-tag--mini .el-icon-close{margin-left:-3px;-webkit-transform:scale(.7);transform:scale(.7)}.custom-theme .el-select-dropdown__item{font-size:14px;padding:0 20px;position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#5a5e66;height:34px;line-height:34px;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:pointer}.custom-theme .el-select-dropdown__item.is-disabled{color:#b4bccc;cursor:not-allowed}.custom-theme .el-select-dropdown__item.is-disabled:hover{background-color:#fff}.custom-theme .el-select-dropdown__item.hover,.custom-theme .el-select-dropdown__item:hover{background-color:#f5f7fa}.custom-theme .el-select-dropdown__item.selected{color:#262729;font-weight:700}.custom-theme .el-select-dropdown__item span{line-height:34px!important}.custom-theme .el-select-group{margin:0;padding:0}.custom-theme .el-select-group__wrap{position:relative;list-style:none;margin:0;padding:0}.custom-theme .el-select-group__wrap:not(:last-of-type){padding-bottom:24px}.custom-theme .el-select-group__wrap:not(:last-of-type)::after{content:'';position:absolute;display:block;left:20px;right:20px;bottom:12px;height:1px;background:#dfe4ed}.custom-theme .el-select-group__title{padding-left:20px;font-size:12px;color:#0a76a4;line-height:30px}.custom-theme .el-select-group .el-select-dropdown__item{padding-left:20px}.custom-theme .el-scrollbar{overflow:hidden;position:relative}.custom-theme .el-scrollbar:active>.el-scrollbar__bar,.custom-theme .el-scrollbar:focus>.el-scrollbar__bar,.custom-theme .el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity 340ms ease-out;transition:opacity 340ms ease-out}.custom-theme .el-scrollbar__wrap{overflow:scroll;height:100%}.custom-theme .el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.custom-theme .el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(135,141,153,.3);-webkit-transition:.3s background-color;transition:.3s background-color}.custom-theme .el-scrollbar__thumb:hover{background-color:rgba(135,141,153,.5)}.custom-theme .el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity 120ms ease-out;transition:opacity 120ms ease-out}.custom-theme .el-scrollbar__bar.is-vertical{width:6px;top:2px}.custom-theme .el-scrollbar__bar.is-vertical>div{width:100%}.custom-theme .el-scrollbar__bar.is-horizontal{height:6px;left:2px}.custom-theme .el-scrollbar__bar.is-horizontal>div{height:100%}.custom-theme .el-select{display:inline-block;position:relative}.custom-theme .el-select:hover .el-input__inner{border-color:#b4bccc}.custom-theme .el-select .el-input__inner{cursor:pointer;padding-right:35px}.custom-theme .el-select .el-input__inner:focus{border-color:#262729}.custom-theme .el-select .el-input .el-select__caret{color:#b4bccc;font-size:14px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg);line-height:16px;cursor:pointer}.custom-theme .el-select .el-input .el-select__caret.is-reverse{-webkit-transform:rotateZ(0);transform:rotateZ(0)}.custom-theme .el-select .el-input .el-select__caret.is-show-close{font-size:14px;text-align:center;-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg);border-radius:100%;color:#b4bccc;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-select .el-input .el-select__caret.is-show-close:hover{color:#878d99}.custom-theme .el-select .el-input.is-disabled .el-input__inner{cursor:not-allowed}.custom-theme .el-select .el-input.is-disabled .el-input__inner:hover{border-color:#dfe4ed}.custom-theme .el-select>.el-input{display:block}.custom-theme .el-select__input{border:none;outline:0;padding:0;margin-left:15px;color:#666;font-size:14px;vertical-align:baseline;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:28px;background-color:transparent}.custom-theme .el-select__input.is-mini{height:14px}.custom-theme .el-select__close{cursor:pointer;position:absolute;top:8px;z-index:1000;right:25px;color:#b4bccc;line-height:18px;font-size:14px}.custom-theme .el-select__close:hover{color:#878d99}.custom-theme .el-select__tags{position:absolute;line-height:normal;white-space:normal;z-index:1;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.custom-theme .el-select .el-tag__close{margin-top:-2px}.custom-theme .el-select .el-tag{-webkit-box-sizing:border-box;box-sizing:border-box;border-color:transparent;margin:3px 0 3px 6px;background-color:#f0f2f5}.custom-theme .el-select .el-tag__close.el-icon-close{background-color:#b4bccc;right:-7px;color:#fff}.custom-theme .el-select .el-tag__close.el-icon-close:hover{background-color:#878d99}.custom-theme .el-select .el-tag__close.el-icon-close::before{display:block;-webkit-transform:translate(0,.5px);transform:translate(0,.5px)}.custom-theme .el-select__tag{display:inline-block;height:24px;line-height:24px;font-size:14px;border-radius:4px;color:#fff;background-color:#262729}.custom-theme .el-select__tag .el-icon-close{font-size:14px}.custom-theme .el-pagination{white-space:nowrap;padding:2px 5px;color:#2d2f33;font-weight:700}.custom-theme .el-pagination::after,.custom-theme .el-pagination::before{display:table;content:\"\"}.custom-theme .el-pagination::after{clear:both}.custom-theme .el-pagination button,.custom-theme .el-pagination span:not([class*=suffix]){display:inline-block;font-size:13px;min-width:35.5px;height:28px;line-height:28px;vertical-align:top;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-pagination .el-input__inner{text-align:center}.custom-theme .el-pagination .el-input__suffix{right:0;-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-pagination .el-select .el-input{width:100px;margin:0 5px}.custom-theme .el-pagination .el-select .el-input .el-input__inner{padding-right:25px;border-radius:3px;height:28px}.custom-theme .el-pagination button{border:none;padding:0 6px;background:0 0}.custom-theme .el-pagination button:focus{outline:0}.custom-theme .el-pagination button:hover{color:#262729}.custom-theme .el-pagination button.disabled{color:#b4bccc;background-color:#fff;cursor:not-allowed}.custom-theme .el-pagination .btn-next,.custom-theme .el-pagination .btn-prev{background:center center no-repeat;background-size:16px;background-color:#fff;cursor:pointer;margin:0;color:#2d2f33}.custom-theme .el-pagination .btn-next .el-icon,.custom-theme .el-pagination .btn-prev .el-icon{display:block;font-size:12px}.custom-theme .el-pagination .btn-prev{padding-right:12px}.custom-theme .el-pagination .btn-next{padding-left:12px}.custom-theme .el-pagination--small .btn-next,.custom-theme .el-pagination--small .btn-prev,.custom-theme .el-pagination--small .el-pager li,.custom-theme .el-pagination--small .el-pager li:last-child{border-color:transparent;font-size:12px;line-height:22px;height:22px;min-width:22px}.custom-theme .el-pagination--small .arrow.disabled{visibility:hidden}.custom-theme .el-pagination__sizes{margin:0 10px 0 0;font-weight:400;color:#5a5e66}.custom-theme .el-pagination__sizes .el-input .el-input__inner{font-size:13px;padding-left:8px}.custom-theme .el-pagination__sizes .el-input .el-input__inner:hover{border-color:#262729}.custom-theme .el-pagination__total{margin-right:10px;font-weight:400;color:#5a5e66}.custom-theme .el-pagination__jump{margin-left:24px;font-weight:400;color:#5a5e66}.custom-theme .el-pagination__jump .el-input__inner{padding:0 3px}.custom-theme .el-pagination__rightwrapper{float:right}.custom-theme .el-pagination__editor{line-height:18px;padding:0 2px;height:28px;text-align:center;margin:0 2px;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:3px;-moz-appearance:textfield}.custom-theme .el-pagination__editor.el-input{width:50px}.custom-theme .el-pagination__editor.el-input .el-input__inner{height:28px}.custom-theme .el-pagination__editor .el-input__inner::-webkit-inner-spin-button,.custom-theme .el-pagination__editor .el-input__inner::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.custom-theme .el-pager{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;list-style:none;display:inline-block;vertical-align:top;font-size:0;padding:0;margin:0}.custom-theme .el-pager .el-icon-more::before{vertical-align:-4px}.custom-theme .el-pager li{padding:0 4px;background:#fff;vertical-align:top;display:inline-block;font-size:13px;min-width:35.5px;height:28px;line-height:28px;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center;margin:0}.custom-theme .el-pager li.btn-quicknext,.custom-theme .el-pager li.btn-quickprev{line-height:28px;color:#2d2f33}.custom-theme .el-pager li.btn-quickprev:hover{cursor:pointer}.custom-theme .el-pager li.btn-quicknext:hover{cursor:pointer}.custom-theme .el-pager li.active+li{border-left:0}.custom-theme .el-pager li:hover{color:#262729}.custom-theme .el-pager li.active{color:#262729;cursor:default}.custom-theme .v-modal-enter{-webkit-animation:v-modal-in .2s ease;animation:v-modal-in .2s ease}.custom-theme .v-modal-leave{-webkit-animation:v-modal-out .2s ease forwards;animation:v-modal-out .2s ease forwards}@-webkit-keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-in{0%{opacity:0}}@-webkit-keyframes v-modal-out{100%{opacity:0}}@keyframes v-modal-out{100%{opacity:0}}.custom-theme .v-modal{position:fixed;left:0;top:0;width:100%;height:100%;opacity:.5;background:#000}.custom-theme .el-dialog{position:relative;margin:0 auto 50px;background:#fff;border-radius:2px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.3);box-shadow:0 1px 3px rgba(0,0,0,.3);-webkit-box-sizing:border-box;box-sizing:border-box;width:50%}.custom-theme .el-dialog.is-fullscreen{width:100%;margin-top:0;margin-bottom:0;height:100%;overflow:auto}.custom-theme .el-dialog__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;margin:0}.custom-theme .el-dialog__header{padding:15px;padding-bottom:10px}.custom-theme .el-dialog__headerbtn{position:absolute;top:15px;right:15px;padding:0;background:0 0;border:none;outline:0;cursor:pointer;font-size:16px}.custom-theme .el-dialog__headerbtn .el-dialog__close{color:#0a76a4}.custom-theme .el-dialog__headerbtn:focus .el-dialog__close,.custom-theme .el-dialog__headerbtn:hover .el-dialog__close{color:#262729}.custom-theme .el-dialog__title{line-height:24px;font-size:18px;color:#2d2f33}.custom-theme .el-dialog__body{padding:30px 20px;color:#5a5e66;line-height:24px;font-size:14px}.custom-theme .el-dialog__footer{padding:15px;padding-top:10px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-dialog--center{text-align:center}.custom-theme .el-dialog--center .el-dialog__header{padding-top:30px}.custom-theme .el-dialog--center .el-dialog__body{text-align:initial;padding:25px 27px 30px}.custom-theme .el-dialog--center .el-dialog__footer{text-align:inherit;padding-bottom:30px}.custom-theme .dialog-fade-enter-active{-webkit-animation:dialog-fade-in .3s;animation:dialog-fade-in .3s}.custom-theme .dialog-fade-leave-active{-webkit-animation:dialog-fade-out .3s;animation:dialog-fade-out .3s}@-webkit-keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes dialog-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes dialog-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.custom-theme .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.custom-theme .el-input::-webkit-scrollbar{z-index:11;width:6px}.custom-theme .el-input::-webkit-scrollbar:horizontal{height:6px}.custom-theme .el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.custom-theme .el-input::-webkit-scrollbar-corner{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.custom-theme .el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.custom-theme .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input__inner:hover{border-color:#b4bccc}.custom-theme .el-input__inner:focus{outline:0;border-color:#262729}.custom-theme .el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.custom-theme .el-input__suffix-inner{pointer-events:all}.custom-theme .el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s}.custom-theme .el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.custom-theme .el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.custom-theme .el-input__validateIcon{pointer-events:none}.custom-theme .el-input.is-active .el-input__inner{outline:0;border-color:#262729}.custom-theme .el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__icon{cursor:not-allowed}.custom-theme .el-input--suffix .el-input__inner{padding-right:30px}.custom-theme .el-input--prefix .el-input__inner{padding-left:30px}.custom-theme .el-input--medium{font-size:14px}.custom-theme .el-input--medium .el-input__inner{height:36px}.custom-theme .el-input--medium .el-input__icon{line-height:36px}.custom-theme .el-input--small{font-size:13px}.custom-theme .el-input--small .el-input__inner{height:32px}.custom-theme .el-input--small .el-input__icon{line-height:32px}.custom-theme .el-input--mini{font-size:12px}.custom-theme .el-input--mini .el-input__inner{height:28px}.custom-theme .el-input--mini .el-input__icon{line-height:28px}.custom-theme .el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.custom-theme .el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.custom-theme .el-input-group__append,.custom-theme .el-input-group__prepend{background-color:#f5f7fa;color:#0a76a4;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.custom-theme .el-input-group__append:focus,.custom-theme .el-input-group__prepend:focus{outline:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-select,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-select{display:inline-block;margin:-20px}.custom-theme .el-input-group__append button.el-button,.custom-theme .el-input-group__append div.el-select .el-input__inner,.custom-theme .el-input-group__append div.el-select:hover .el-input__inner,.custom-theme .el-input-group__prepend button.el-button,.custom-theme .el-input-group__prepend div.el-select .el-input__inner,.custom-theme .el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-input,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-input{font-size:inherit}.custom-theme .el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-textarea{display:inline-block;width:100%;vertical-align:bottom}.custom-theme .el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:hover{border-color:#b4bccc}.custom-theme .el-textarea__inner:focus{outline:0;border-color:#262729}.custom-theme .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-scrollbar{overflow:hidden;position:relative}.custom-theme .el-scrollbar:active>.el-scrollbar__bar,.custom-theme .el-scrollbar:focus>.el-scrollbar__bar,.custom-theme .el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity 340ms ease-out;transition:opacity 340ms ease-out}.custom-theme .el-scrollbar__wrap{overflow:scroll;height:100%}.custom-theme .el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.custom-theme .el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(135,141,153,.3);-webkit-transition:.3s background-color;transition:.3s background-color}.custom-theme .el-scrollbar__thumb:hover{background-color:rgba(135,141,153,.5)}.custom-theme .el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity 120ms ease-out;transition:opacity 120ms ease-out}.custom-theme .el-scrollbar__bar.is-vertical{width:6px;top:2px}.custom-theme .el-scrollbar__bar.is-vertical>div{width:100%}.custom-theme .el-scrollbar__bar.is-horizontal{height:6px;left:2px}.custom-theme .el-scrollbar__bar.is-horizontal>div{height:100%}.custom-theme .el-popper .popper__arrow,.custom-theme .el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.custom-theme .el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.custom-theme .el-popper .popper__arrow::after{content:\" \";border-width:6px}.custom-theme .el-popper[x-placement^=top]{margin-bottom:12px}.custom-theme .el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.custom-theme .el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.custom-theme .el-popper[x-placement^=bottom]{margin-top:12px}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.custom-theme .el-popper[x-placement^=right]{margin-left:12px}.custom-theme .el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.custom-theme .el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.custom-theme .el-popper[x-placement^=left]{margin-right:12px}.custom-theme .el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.custom-theme .el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.custom-theme .el-autocomplete{position:relative;display:inline-block}.custom-theme .el-autocomplete-suggestion{margin:5px 0;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:4px}.custom-theme .el-autocomplete-suggestion.el-popper .popper__arrow{left:24px!important}.custom-theme .el-autocomplete-suggestion__wrap{max-height:280px;padding:10px 0;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:auto;background-color:#fff;border:1px solid #dfe4ed;border-radius:4px}.custom-theme .el-autocomplete-suggestion__list{margin:0;padding:0}.custom-theme .el-autocomplete-suggestion li{padding:0 20px;margin:0;line-height:34px;cursor:pointer;color:#5a5e66;font-size:14px;list-style:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.custom-theme .el-autocomplete-suggestion li:hover{background-color:#f5f7fa}.custom-theme .el-autocomplete-suggestion li.highlighted{background-color:#f5f7fa}.custom-theme .el-autocomplete-suggestion li.divider{margin-top:6px;border-top:1px solid #000}.custom-theme .el-autocomplete-suggestion li.divider:last-child{margin-bottom:-6px}.custom-theme .el-autocomplete-suggestion.is-loading li{text-align:center;height:100px;line-height:100px;font-size:20px;color:#999}.custom-theme .el-autocomplete-suggestion.is-loading li::after{display:inline-block;content:\"\";height:100%;vertical-align:middle}.custom-theme .el-autocomplete-suggestion.is-loading li:hover{background-color:#fff}.custom-theme .el-autocomplete-suggestion.is-loading .el-icon-loading{vertical-align:middle}.custom-theme .el-button{display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;background:#fff;border:1px solid #d8dce5;border-color:#d8dce5;color:#5a5e66;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:.1s;transition:.1s;font-weight:500;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:14px;border-radius:4px}.custom-theme .el-button+.el-button{margin-left:10px}.custom-theme .el-button.is-round{padding:12px 20px}.custom-theme .el-button:focus,.custom-theme .el-button:hover{color:#262729;border-color:#bebebf;background-color:#e9e9ea}.custom-theme .el-button:active{color:#222325;border-color:#222325;outline:0}.custom-theme .el-button::-moz-focus-inner{border:0}.custom-theme .el-button [class*=el-icon-]+span{margin-left:5px}.custom-theme .el-button.is-plain:focus,.custom-theme .el-button.is-plain:hover{background:#fff;border-color:#262729;color:#262729}.custom-theme .el-button.is-plain:active{background:#fff;border-color:#222325;color:#222325;outline:0}.custom-theme .el-button.is-active{color:#222325;border-color:#222325}.custom-theme .el-button.is-disabled,.custom-theme .el-button.is-disabled:focus,.custom-theme .el-button.is-disabled:hover{color:#b4bccc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#e6ebf5}.custom-theme .el-button.is-disabled.el-button--text{background-color:transparent}.custom-theme .el-button.is-disabled.is-plain,.custom-theme .el-button.is-disabled.is-plain:focus,.custom-theme .el-button.is-disabled.is-plain:hover{background-color:#fff;border-color:#e6ebf5;color:#b4bccc}.custom-theme .el-button.is-loading{position:relative;pointer-events:none}.custom-theme .el-button.is-loading:before{pointer-events:none;content:'';position:absolute;left:-1px;top:-1px;right:-1px;bottom:-1px;border-radius:inherit;background-color:rgba(255,255,255,.35)}.custom-theme .el-button.is-round{border-radius:20px;padding:12px 23px}.custom-theme .el-button--primary{color:#fff;background-color:#262729;border-color:#262729}.custom-theme .el-button--primary:focus,.custom-theme .el-button--primary:hover{background:#515254;border-color:#515254;color:#fff}.custom-theme .el-button--primary:active{background:#222325;border-color:#222325;color:#fff;outline:0}.custom-theme .el-button--primary.is-active{background:#222325;border-color:#222325;color:#fff}.custom-theme .el-button--primary.is-disabled,.custom-theme .el-button--primary.is-disabled:active,.custom-theme .el-button--primary.is-disabled:focus,.custom-theme .el-button--primary.is-disabled:hover{color:#fff;background-color:#939394;border-color:#939394}.custom-theme .el-button--primary.is-plain{color:#262729;background:#e9e9ea;border-color:#a8a9a9}.custom-theme .el-button--primary.is-plain:focus,.custom-theme .el-button--primary.is-plain:hover{background:#262729;border-color:#262729;color:#fff}.custom-theme .el-button--primary.is-plain:active{background:#222325;border-color:#222325;color:#fff;outline:0}.custom-theme .el-button--primary.is-plain.is-disabled,.custom-theme .el-button--primary.is-plain.is-disabled:active,.custom-theme .el-button--primary.is-plain.is-disabled:focus,.custom-theme .el-button--primary.is-plain.is-disabled:hover{color:#7d7d7f;background-color:#e9e9ea;border-color:#d4d4d4}.custom-theme .el-button--success{color:#fff;background-color:#409167;border-color:#409167}.custom-theme .el-button--success:focus,.custom-theme .el-button--success:hover{background:#66a785;border-color:#66a785;color:#fff}.custom-theme .el-button--success:active{background:#3a835d;border-color:#3a835d;color:#fff;outline:0}.custom-theme .el-button--success.is-active{background:#3a835d;border-color:#3a835d;color:#fff}.custom-theme .el-button--success.is-disabled,.custom-theme .el-button--success.is-disabled:active,.custom-theme .el-button--success.is-disabled:focus,.custom-theme .el-button--success.is-disabled:hover{color:#fff;background-color:#a0c8b3;border-color:#a0c8b3}.custom-theme .el-button--success.is-plain{color:#409167;background:#ecf4f0;border-color:#b3d3c2}.custom-theme .el-button--success.is-plain:focus,.custom-theme .el-button--success.is-plain:hover{background:#409167;border-color:#409167;color:#fff}.custom-theme .el-button--success.is-plain:active{background:#3a835d;border-color:#3a835d;color:#fff;outline:0}.custom-theme .el-button--success.is-plain.is-disabled,.custom-theme .el-button--success.is-plain.is-disabled:active,.custom-theme .el-button--success.is-plain.is-disabled:focus,.custom-theme .el-button--success.is-plain.is-disabled:hover{color:#8cbda4;background-color:#ecf4f0;border-color:#d9e9e1}.custom-theme .el-button--warning{color:#fff;background-color:#9da408;border-color:#9da408}.custom-theme .el-button--warning:focus,.custom-theme .el-button--warning:hover{background:#b1b639;border-color:#b1b639;color:#fff}.custom-theme .el-button--warning:active{background:#8d9407;border-color:#8d9407;color:#fff;outline:0}.custom-theme .el-button--warning.is-active{background:#8d9407;border-color:#8d9407;color:#fff}.custom-theme .el-button--warning.is-disabled,.custom-theme .el-button--warning.is-disabled:active,.custom-theme .el-button--warning.is-disabled:focus,.custom-theme .el-button--warning.is-disabled:hover{color:#fff;background-color:#ced284;border-color:#ced284}.custom-theme .el-button--warning.is-plain{color:#9da408;background:#f5f6e6;border-color:#d8db9c}.custom-theme .el-button--warning.is-plain:focus,.custom-theme .el-button--warning.is-plain:hover{background:#9da408;border-color:#9da408;color:#fff}.custom-theme .el-button--warning.is-plain:active{background:#8d9407;border-color:#8d9407;color:#fff;outline:0}.custom-theme .el-button--warning.is-plain.is-disabled,.custom-theme .el-button--warning.is-plain.is-disabled:active,.custom-theme .el-button--warning.is-plain.is-disabled:focus,.custom-theme .el-button--warning.is-plain.is-disabled:hover{color:#c4c86b;background-color:#f5f6e6;border-color:#ebedce}.custom-theme .el-button--danger{color:#fff;background-color:#b3450e;border-color:#b3450e}.custom-theme .el-button--danger:focus,.custom-theme .el-button--danger:hover{background:#c26a3e;border-color:#c26a3e;color:#fff}.custom-theme .el-button--danger:active{background:#a13e0d;border-color:#a13e0d;color:#fff;outline:0}.custom-theme .el-button--danger.is-active{background:#a13e0d;border-color:#a13e0d;color:#fff}.custom-theme .el-button--danger.is-disabled,.custom-theme .el-button--danger.is-disabled:active,.custom-theme .el-button--danger.is-disabled:focus,.custom-theme .el-button--danger.is-disabled:hover{color:#fff;background-color:#d9a287;border-color:#d9a287}.custom-theme .el-button--danger.is-plain{color:#b3450e;background:#f7ece7;border-color:#e1b59f}.custom-theme .el-button--danger.is-plain:focus,.custom-theme .el-button--danger.is-plain:hover{background:#b3450e;border-color:#b3450e;color:#fff}.custom-theme .el-button--danger.is-plain:active{background:#a13e0d;border-color:#a13e0d;color:#fff;outline:0}.custom-theme .el-button--danger.is-plain.is-disabled,.custom-theme .el-button--danger.is-plain.is-disabled:active,.custom-theme .el-button--danger.is-plain.is-disabled:focus,.custom-theme .el-button--danger.is-plain.is-disabled:hover{color:#d18f6e;background-color:#f7ece7;border-color:#f0dacf}.custom-theme .el-button--info{color:#fff;background-color:#0a76a4;border-color:#0a76a4}.custom-theme .el-button--info:focus,.custom-theme .el-button--info:hover{background:#3b91b6;border-color:#3b91b6;color:#fff}.custom-theme .el-button--info:active{background:#096a94;border-color:#096a94;color:#fff;outline:0}.custom-theme .el-button--info.is-active{background:#096a94;border-color:#096a94;color:#fff}.custom-theme .el-button--info.is-disabled,.custom-theme .el-button--info.is-disabled:active,.custom-theme .el-button--info.is-disabled:focus,.custom-theme .el-button--info.is-disabled:hover{color:#fff;background-color:#85bbd2;border-color:#85bbd2}.custom-theme .el-button--info.is-plain{color:#0a76a4;background:#e7f1f6;border-color:#9dc8db}.custom-theme .el-button--info.is-plain:focus,.custom-theme .el-button--info.is-plain:hover{background:#0a76a4;border-color:#0a76a4;color:#fff}.custom-theme .el-button--info.is-plain:active{background:#096a94;border-color:#096a94;color:#fff;outline:0}.custom-theme .el-button--info.is-plain.is-disabled,.custom-theme .el-button--info.is-plain.is-disabled:active,.custom-theme .el-button--info.is-plain.is-disabled:focus,.custom-theme .el-button--info.is-plain.is-disabled:hover{color:#6cadc8;background-color:#e7f1f6;border-color:#cee4ed}.custom-theme .el-button--medium{padding:10px 20px;font-size:14px;border-radius:4px}.custom-theme .el-button--medium.is-round{padding:10px 20px}.custom-theme .el-button--small{padding:9px 15px;font-size:12px;border-radius:3px}.custom-theme .el-button--small.is-round{padding:9px 15px}.custom-theme .el-button--mini{padding:7px 15px;font-size:12px;border-radius:3px}.custom-theme .el-button--mini.is-round{padding:7px 15px}.custom-theme .el-button--text{border:none;color:#262729;background:0 0;padding-left:0;padding-right:0}.custom-theme .el-button--text:focus,.custom-theme .el-button--text:hover{color:#515254;border-color:transparent;background-color:transparent}.custom-theme .el-button--text:active{color:#222325;border-color:transparent;background-color:transparent}.custom-theme .el-button-group{display:inline-block;vertical-align:middle}.custom-theme .el-button-group::after,.custom-theme .el-button-group::before{display:table;content:\"\"}.custom-theme .el-button-group::after{clear:both}.custom-theme .el-button-group .el-button{float:left;position:relative}.custom-theme .el-button-group .el-button+.el-button{margin-left:0}.custom-theme .el-button-group .el-button:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-button-group .el-button:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-button-group .el-button:not(:first-child):not(:last-child){border-radius:0}.custom-theme .el-button-group .el-button:not(:last-child){margin-right:-1px}.custom-theme .el-button-group .el-button:active,.custom-theme .el-button-group .el-button:focus,.custom-theme .el-button-group .el-button:hover{z-index:1}.custom-theme .el-button-group .el-button.is-active{z-index:1}.custom-theme .el-button-group .el-button--primary:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--primary:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--primary:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-popper .popper__arrow,.custom-theme .el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.custom-theme .el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.custom-theme .el-popper .popper__arrow::after{content:\" \";border-width:6px}.custom-theme .el-popper[x-placement^=top]{margin-bottom:12px}.custom-theme .el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.custom-theme .el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.custom-theme .el-popper[x-placement^=bottom]{margin-top:12px}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.custom-theme .el-popper[x-placement^=right]{margin-left:12px}.custom-theme .el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.custom-theme .el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.custom-theme .el-popper[x-placement^=left]{margin-right:12px}.custom-theme .el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.custom-theme .el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.custom-theme .el-dropdown{display:inline-block;position:relative;color:#5a5e66;font-size:14px}.custom-theme .el-dropdown .el-button-group{display:block}.custom-theme .el-dropdown .el-button-group .el-button{float:none}.custom-theme .el-dropdown .el-dropdown__caret-button{padding-left:5px;padding-right:5px;position:relative;border-left:none}.custom-theme .el-dropdown .el-dropdown__caret-button::before{content:'';position:absolute;display:block;width:1px;top:5px;bottom:5px;left:0;background:rgba(255,255,255,.5)}.custom-theme .el-dropdown .el-dropdown__caret-button:hover::before{top:0;bottom:0}.custom-theme .el-dropdown .el-dropdown__caret-button .el-dropdown__icon{padding-left:0}.custom-theme .el-dropdown__icon{font-size:12px;margin:0 3px}.custom-theme .el-dropdown-menu{position:absolute;top:0;left:0;z-index:10;padding:10px 0;margin:5px 0;background-color:#fff;border:1px solid #e6ebf5;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.custom-theme .el-dropdown-menu__item{list-style:none;line-height:36px;padding:0 20px;margin:0;font-size:14px;color:#5a5e66;cursor:pointer}.custom-theme .el-dropdown-menu__item:not(.is-disabled):hover{background-color:#e9e9ea;color:#515254}.custom-theme .el-dropdown-menu__item--divided{position:relative;margin-top:6px;border-top:1px solid #e6ebf5}.custom-theme .el-dropdown-menu__item--divided:before{content:'';height:6px;display:block;margin:0 -20px;background-color:#fff}.custom-theme .el-dropdown-menu__item.is-disabled{cursor:default;color:#bbb;pointer-events:none}.custom-theme .el-dropdown-menu--medium{padding:6px 0}.custom-theme .el-dropdown-menu--medium .el-dropdown-menu__item{line-height:30px;padding:0 17px;font-size:14px}.custom-theme .el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:6px}.custom-theme .el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:6px;margin:0 -17px}.custom-theme .el-dropdown-menu--small{padding:6px 0}.custom-theme .el-dropdown-menu--small .el-dropdown-menu__item{line-height:27px;padding:0 15px;font-size:13px}.custom-theme .el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:4px}.custom-theme .el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:4px;margin:0 -15px}.custom-theme .el-dropdown-menu--mini{padding:3px 0}.custom-theme .el-dropdown-menu--mini .el-dropdown-menu__item{line-height:24px;padding:0 10px;font-size:12px}.custom-theme .el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:3px}.custom-theme .el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:3px;margin:0 -10px}.custom-theme .el-menu{border-right:solid 1px #e6e6e6;list-style:none;position:relative;margin:0;padding-left:0;background-color:#fff}.custom-theme .el-menu::after,.custom-theme .el-menu::before{display:table;content:\"\"}.custom-theme .el-menu::after{clear:both}.custom-theme .el-menu li{list-style:none}.custom-theme .el-menu--horizontal{border-right:none;border-bottom:solid 1px #e6e6e6}.custom-theme .el-menu--horizontal .el-menu-item{float:left;height:60px;line-height:60px;margin:0;cursor:pointer;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;border-bottom:2px solid transparent;color:#878d99}.custom-theme .el-menu--horizontal .el-menu-item a,.custom-theme .el-menu--horizontal .el-menu-item a:hover{color:inherit}.custom-theme .el-menu--horizontal .el-menu-item:focus,.custom-theme .el-menu--horizontal .el-menu-item:hover{background-color:#fff}.custom-theme .el-menu--horizontal .el-submenu{float:left;position:relative}.custom-theme .el-menu--horizontal .el-submenu:focus{outline:0}.custom-theme .el-menu--horizontal .el-submenu:focus>.el-submenu__title{color:#2d2f33}.custom-theme .el-menu--horizontal .el-submenu>.el-menu{position:absolute;top:65px;left:0;border:none;padding:5px 0;background-color:#fff;z-index:100;min-width:100%;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:2px}.custom-theme .el-menu--horizontal .el-submenu .el-submenu__title{height:60px;line-height:60px;border-bottom:2px solid transparent;color:#878d99}.custom-theme .el-menu--horizontal .el-submenu .el-submenu__title:hover{background-color:#fff}.custom-theme .el-menu--horizontal .el-submenu .el-menu-item{background-color:#fff;float:none;height:36px;line-height:36px;padding:0 10px}.custom-theme .el-menu--horizontal .el-submenu .el-submenu__icon-arrow{position:static;vertical-align:middle;margin-left:8px;margin-top:-3px}.custom-theme .el-menu--horizontal .el-menu-item:focus,.custom-theme .el-menu--horizontal .el-menu-item:hover,.custom-theme .el-menu--horizontal .el-submenu__title:hover{outline:0;color:#2d2f33}.custom-theme .el-menu--horizontal>.el-menu-item.is-active,.custom-theme .el-menu--horizontal>.el-submenu.is-active .el-submenu__title{border-bottom:2px solid #262729;color:#2d2f33}.custom-theme .el-menu--collapse{width:64px}.custom-theme .el-menu--collapse>.el-menu-item [class^=el-icon-],.custom-theme .el-menu--collapse>.el-submenu>.el-submenu__title [class^=el-icon-]{margin:0;vertical-align:middle;width:24px;text-align:center}.custom-theme .el-menu--collapse>.el-menu-item .el-submenu__icon-arrow,.custom-theme .el-menu--collapse>.el-submenu>.el-submenu__title .el-submenu__icon-arrow{display:none}.custom-theme .el-menu--collapse>.el-menu-item span,.custom-theme .el-menu--collapse>.el-submenu>.el-submenu__title span{height:0;width:0;overflow:hidden;visibility:hidden;display:inline-block}.custom-theme .el-menu--collapse>.el-menu-item.is-active i{color:inherit}.custom-theme .el-menu--collapse .el-menu .el-submenu{min-width:200px}.custom-theme .el-menu--collapse .el-submenu{position:relative}.custom-theme .el-menu--collapse .el-submenu .el-menu{position:absolute;margin-left:5px;top:0;left:100%;z-index:10;border:1px solid #dfe4ed;border-radius:2px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.custom-theme .el-menu--collapse .el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:none;transform:none}.custom-theme .el-menu-item{height:56px;line-height:56px;font-size:14px;color:#2d2f33;padding:0 20px;cursor:pointer;position:relative;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;-webkit-box-sizing:border-box;box-sizing:border-box;white-space:nowrap}.custom-theme .el-menu-item [class^=el-icon-]{margin-right:5px;width:24px;text-align:center;font-size:18px}.custom-theme .el-menu-item *{vertical-align:middle}.custom-theme .el-menu-item:first-child{margin-left:0}.custom-theme .el-menu-item:last-child{margin-right:0}.custom-theme .el-menu-item:focus,.custom-theme .el-menu-item:hover{outline:0;background-color:#e9e9ea}.custom-theme .el-menu-item i{color:#878d99}.custom-theme .el-menu-item.is-active{color:#262729}.custom-theme .el-menu-item.is-active i{color:inherit}.custom-theme .el-submenu__title{position:relative;height:56px;line-height:56px;font-size:14px;color:#2d2f33;padding:0 20px;cursor:pointer;position:relative;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;-webkit-box-sizing:border-box;box-sizing:border-box;white-space:nowrap}.custom-theme .el-submenu__title *{vertical-align:middle}.custom-theme .el-submenu__title i{color:#878d99}.custom-theme .el-submenu__title:hover{background-color:#e9e9ea}.custom-theme .el-submenu .el-menu{border:none}.custom-theme .el-submenu .el-menu-item{height:50px;line-height:50px;padding:0 45px;min-width:200px}.custom-theme .el-submenu__icon-arrow{position:absolute;top:50%;right:20px;margin-top:-7px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-size:12px}.custom-theme .el-submenu.is-active .el-submenu__title{border-bottom-color:#262729}.custom-theme .el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg)}.custom-theme .el-submenu [class^=el-icon-]{vertical-align:middle;margin-right:5px;width:24px;text-align:center;font-size:18px}.custom-theme .el-menu-item-group>ul{padding:0}.custom-theme .el-menu-item-group__title{padding:7px 0 7px 20px;line-height:normal;font-size:12px;color:#878d99}.custom-theme .horizontal-collapse-transition .el-submenu__title .el-submenu__icon-arrow{-webkit-transition:.2s;transition:.2s;opacity:0}.custom-theme .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.custom-theme .el-input::-webkit-scrollbar{z-index:11;width:6px}.custom-theme .el-input::-webkit-scrollbar:horizontal{height:6px}.custom-theme .el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.custom-theme .el-input::-webkit-scrollbar-corner{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.custom-theme .el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.custom-theme .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input__inner:hover{border-color:#b4bccc}.custom-theme .el-input__inner:focus{outline:0;border-color:#262729}.custom-theme .el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.custom-theme .el-input__suffix-inner{pointer-events:all}.custom-theme .el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s}.custom-theme .el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.custom-theme .el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.custom-theme .el-input__validateIcon{pointer-events:none}.custom-theme .el-input.is-active .el-input__inner{outline:0;border-color:#262729}.custom-theme .el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__icon{cursor:not-allowed}.custom-theme .el-input--suffix .el-input__inner{padding-right:30px}.custom-theme .el-input--prefix .el-input__inner{padding-left:30px}.custom-theme .el-input--medium{font-size:14px}.custom-theme .el-input--medium .el-input__inner{height:36px}.custom-theme .el-input--medium .el-input__icon{line-height:36px}.custom-theme .el-input--small{font-size:13px}.custom-theme .el-input--small .el-input__inner{height:32px}.custom-theme .el-input--small .el-input__icon{line-height:32px}.custom-theme .el-input--mini{font-size:12px}.custom-theme .el-input--mini .el-input__inner{height:28px}.custom-theme .el-input--mini .el-input__icon{line-height:28px}.custom-theme .el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.custom-theme .el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.custom-theme .el-input-group__append,.custom-theme .el-input-group__prepend{background-color:#f5f7fa;color:#0a76a4;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.custom-theme .el-input-group__append:focus,.custom-theme .el-input-group__prepend:focus{outline:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-select,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-select{display:inline-block;margin:-20px}.custom-theme .el-input-group__append button.el-button,.custom-theme .el-input-group__append div.el-select .el-input__inner,.custom-theme .el-input-group__append div.el-select:hover .el-input__inner,.custom-theme .el-input-group__prepend button.el-button,.custom-theme .el-input-group__prepend div.el-select .el-input__inner,.custom-theme .el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-input,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-input{font-size:inherit}.custom-theme .el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-textarea{display:inline-block;width:100%;vertical-align:bottom}.custom-theme .el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:hover{border-color:#b4bccc}.custom-theme .el-textarea__inner:focus{outline:0;border-color:#262729}.custom-theme .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.custom-theme .el-input::-webkit-scrollbar{z-index:11;width:6px}.custom-theme .el-input::-webkit-scrollbar:horizontal{height:6px}.custom-theme .el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.custom-theme .el-input::-webkit-scrollbar-corner{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.custom-theme .el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.custom-theme .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input__inner:hover{border-color:#b4bccc}.custom-theme .el-input__inner:focus{outline:0;border-color:#262729}.custom-theme .el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.custom-theme .el-input__suffix-inner{pointer-events:all}.custom-theme .el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s}.custom-theme .el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.custom-theme .el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.custom-theme .el-input__validateIcon{pointer-events:none}.custom-theme .el-input.is-active .el-input__inner{outline:0;border-color:#262729}.custom-theme .el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__icon{cursor:not-allowed}.custom-theme .el-input--suffix .el-input__inner{padding-right:30px}.custom-theme .el-input--prefix .el-input__inner{padding-left:30px}.custom-theme .el-input--medium{font-size:14px}.custom-theme .el-input--medium .el-input__inner{height:36px}.custom-theme .el-input--medium .el-input__icon{line-height:36px}.custom-theme .el-input--small{font-size:13px}.custom-theme .el-input--small .el-input__inner{height:32px}.custom-theme .el-input--small .el-input__icon{line-height:32px}.custom-theme .el-input--mini{font-size:12px}.custom-theme .el-input--mini .el-input__inner{height:28px}.custom-theme .el-input--mini .el-input__icon{line-height:28px}.custom-theme .el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.custom-theme .el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.custom-theme .el-input-group__append,.custom-theme .el-input-group__prepend{background-color:#f5f7fa;color:#0a76a4;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.custom-theme .el-input-group__append:focus,.custom-theme .el-input-group__prepend:focus{outline:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-select,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-select{display:inline-block;margin:-20px}.custom-theme .el-input-group__append button.el-button,.custom-theme .el-input-group__append div.el-select .el-input__inner,.custom-theme .el-input-group__append div.el-select:hover .el-input__inner,.custom-theme .el-input-group__prepend button.el-button,.custom-theme .el-input-group__prepend div.el-select .el-input__inner,.custom-theme .el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-input,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-input{font-size:inherit}.custom-theme .el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-textarea{display:inline-block;width:100%;vertical-align:bottom}.custom-theme .el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:hover{border-color:#b4bccc}.custom-theme .el-textarea__inner:focus{outline:0;border-color:#262729}.custom-theme .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-input-number{position:relative;display:inline-block;width:180px;line-height:38px}.custom-theme .el-input-number .el-input{display:block}.custom-theme .el-input-number .el-input__inner{-webkit-appearance:none;padding-left:50px;padding-right:50px;text-align:center}.custom-theme .el-input-number__decrease,.custom-theme .el-input-number__increase{position:absolute;z-index:1;top:1px;width:40px;height:auto;text-align:center;background:#f5f7fa;color:#5a5e66;cursor:pointer;font-size:13px}.custom-theme .el-input-number__decrease:hover,.custom-theme .el-input-number__increase:hover{color:#262729}.custom-theme .el-input-number__decrease:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled),.custom-theme .el-input-number__increase:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled){border-color:#262729}.custom-theme .el-input-number__decrease.is-disabled,.custom-theme .el-input-number__increase.is-disabled{color:#b4bccc;cursor:not-allowed}.custom-theme .el-input-number__increase{right:1px;border-radius:0 4px 4px 0;border-left:1px solid #d8dce5}.custom-theme .el-input-number__decrease{left:1px;border-radius:4px 0 0 4px;border-right:1px solid #d8dce5}.custom-theme .el-input-number.is-disabled .el-input-number__decrease,.custom-theme .el-input-number.is-disabled .el-input-number__increase{border-color:#dfe4ed;color:#dfe4ed}.custom-theme .el-input-number.is-disabled .el-input-number__decrease:hover,.custom-theme .el-input-number.is-disabled .el-input-number__increase:hover{color:#dfe4ed;cursor:not-allowed}.custom-theme .el-input-number--medium{width:200px;line-height:34px}.custom-theme .el-input-number--medium .el-input-number__decrease,.custom-theme .el-input-number--medium .el-input-number__increase{width:36px;font-size:14px}.custom-theme .el-input-number--medium .el-input__inner{padding-left:43px;padding-right:43px}.custom-theme .el-input-number--small{width:130px;line-height:30px}.custom-theme .el-input-number--small .el-input-number__decrease,.custom-theme .el-input-number--small .el-input-number__increase{width:32px;font-size:13px}.custom-theme .el-input-number--small .el-input-number__decrease [class*=el-icon],.custom-theme .el-input-number--small .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.9);transform:scale(.9)}.custom-theme .el-input-number--small .el-input__inner{padding-left:39px;padding-right:39px}.custom-theme .el-input-number--mini{width:130px;line-height:26px}.custom-theme .el-input-number--mini .el-input-number__decrease,.custom-theme .el-input-number--mini .el-input-number__increase{width:28px;font-size:12px}.custom-theme .el-input-number--mini .el-input-number__decrease [class*=el-icon],.custom-theme .el-input-number--mini .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-input-number--mini .el-input__inner{padding-left:35px;padding-right:35px}.custom-theme .el-input-number.is-without-controls .el-input__inner{padding-left:15px;padding-right:15px}.custom-theme .el-input-number.is-controls-right .el-input__inner{padding-left:15px;padding-right:50px}.custom-theme .el-input-number.is-controls-right .el-input-number__decrease,.custom-theme .el-input-number.is-controls-right .el-input-number__increase{height:auto;line-height:19px}.custom-theme .el-input-number.is-controls-right .el-input-number__decrease [class*=el-icon],.custom-theme .el-input-number.is-controls-right .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-input-number.is-controls-right .el-input-number__increase{border-radius:0 4px 0 0;border-bottom:1px solid #d8dce5}.custom-theme .el-input-number.is-controls-right .el-input-number__decrease{right:1px;bottom:1px;top:auto;left:auto;border-right:none;border-left:1px solid #d8dce5;border-radius:0 0 4px 0}.custom-theme .el-input-number.is-controls-right[class*=medium] [class*=decrease],.custom-theme .el-input-number.is-controls-right[class*=medium] [class*=increase]{line-height:17px}.custom-theme .el-input-number.is-controls-right[class*=small] [class*=decrease],.custom-theme .el-input-number.is-controls-right[class*=small] [class*=increase]{line-height:15px}.custom-theme .el-input-number.is-controls-right[class*=mini] [class*=decrease],.custom-theme .el-input-number.is-controls-right[class*=mini] [class*=increase]{line-height:13px}.custom-theme .el-radio{color:#5a5e66;font-weight:500;line-height:1;position:relative;cursor:pointer;display:inline-block;white-space:nowrap;outline:0;font-size:14px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.custom-theme .el-radio.is-bordered{padding:10px 20px 10px 10px;border-radius:4px;border:1px solid #d8dce5}.custom-theme .el-radio.is-bordered.is-checked{border-color:#262729}.custom-theme .el-radio.is-bordered.is-disabled{cursor:not-allowed;border-color:#e6ebf5}.custom-theme .el-radio.is-bordered+.el-radio.is-bordered{margin-left:10px}.custom-theme .el-radio--medium.is-bordered{padding:8px 20px 8px 10px;border-radius:4px}.custom-theme .el-radio--medium.is-bordered .el-radio__label{font-size:14px}.custom-theme .el-radio--medium.is-bordered .el-radio__inner{height:14px;width:14px}.custom-theme .el-radio--small.is-bordered{padding:6px 15px 6px 10px;border-radius:3px}.custom-theme .el-radio--small.is-bordered .el-radio__label{font-size:12px}.custom-theme .el-radio--small.is-bordered .el-radio__inner{height:12px;width:12px}.custom-theme .el-radio--mini.is-bordered{padding:4px 15px 4px 10px;border-radius:3px}.custom-theme .el-radio--mini.is-bordered .el-radio__label{font-size:12px}.custom-theme .el-radio--mini.is-bordered .el-radio__inner{height:12px;width:12px}.custom-theme .el-radio+.el-radio{margin-left:30px}.custom-theme .el-radio__input{white-space:nowrap;cursor:pointer;outline:0;display:inline-block;line-height:1;position:relative;vertical-align:middle}.custom-theme .el-radio__input.is-disabled .el-radio__inner{background-color:#f5f7fa;border-color:#dfe4ed;cursor:not-allowed}.custom-theme .el-radio__input.is-disabled .el-radio__inner::after{cursor:not-allowed;background-color:#f5f7fa}.custom-theme .el-radio__input.is-disabled .el-radio__inner+.el-radio__label{cursor:not-allowed}.custom-theme .el-radio__input.is-disabled.is-checked .el-radio__inner{background-color:#f5f7fa;border-color:#dfe4ed}.custom-theme .el-radio__input.is-disabled.is-checked .el-radio__inner::after{background-color:#b4bccc}.custom-theme .el-radio__input.is-disabled+span.el-radio__label{color:#b4bccc;cursor:not-allowed}.custom-theme .el-radio__input.is-checked .el-radio__inner{border-color:#262729;background:#262729}.custom-theme .el-radio__input.is-checked .el-radio__inner::after{-webkit-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}.custom-theme .el-radio__input.is-checked+.el-radio__label{color:#262729}.custom-theme .el-radio__input.is-focus .el-radio__inner{border-color:#262729}.custom-theme .el-radio__inner{border:1px solid #d8dce5;border-radius:100%;width:14px;height:14px;background-color:#fff;position:relative;cursor:pointer;display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-radio__inner:hover{border-color:#262729}.custom-theme .el-radio__inner::after{width:4px;height:4px;border-radius:100%;background-color:#fff;content:\"\";position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%) scale(0);transform:translate(-50%,-50%) scale(0);-webkit-transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6);transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6);transition:transform .15s cubic-bezier(.71,-.46,.88,.6);transition:transform .15s cubic-bezier(.71,-.46,.88,.6),-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6)}.custom-theme .el-radio__original{opacity:0;outline:0;position:absolute;z-index:-1;top:0;left:0;right:0;bottom:0;margin:0}.custom-theme .el-radio:focus:not(.is-focus):not(:active) .el-radio__inner{-webkit-box-shadow:0 0 2px 2px #262729;box-shadow:0 0 2px 2px #262729}.custom-theme .el-radio__label{font-size:14px;padding-left:10px}.custom-theme .el-radio-group{display:inline-block;line-height:1;vertical-align:middle;font-size:0}.custom-theme .el-radio-button{position:relative;display:inline-block;outline:0}.custom-theme .el-radio-button__inner{display:inline-block;line-height:1;white-space:nowrap;vertical-align:middle;background:#fff;border:1px solid #d8dce5;font-weight:500;border-left:0;color:#5a5e66;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;position:relative;cursor:pointer;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);padding:12px 20px;font-size:14px;border-radius:0}.custom-theme .el-radio-button__inner.is-round{padding:12px 20px}.custom-theme .el-radio-button__inner:hover{color:#262729}.custom-theme .el-radio-button__inner [class*=el-icon-]{line-height:.9}.custom-theme .el-radio-button__inner [class*=el-icon-]+span{margin-left:5px}.custom-theme .el-radio-button__orig-radio{opacity:0;outline:0;position:absolute;z-index:-1;left:-999px}.custom-theme .el-radio-button__orig-radio:checked+.el-radio-button__inner{color:#fff;background-color:#262729;border-color:#262729;-webkit-box-shadow:-1px 0 0 0 #262729;box-shadow:-1px 0 0 0 #262729}.custom-theme .el-radio-button__orig-radio:disabled+.el-radio-button__inner{color:#b4bccc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#e6ebf5;-webkit-box-shadow:none;box-shadow:none}.custom-theme .el-radio-button__orig-radio:disabled:checked+.el-radio-button__inner{background-color:#edf2fc}.custom-theme .el-radio-button:first-child .el-radio-button__inner{border-left:1px solid #d8dce5;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.custom-theme .el-radio-button:last-child .el-radio-button__inner{border-radius:0 4px 4px 0}.custom-theme .el-radio-button:first-child:last-child .el-radio-button__inner{border-radius:4px}.custom-theme .el-radio-button--medium .el-radio-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.custom-theme .el-radio-button--medium .el-radio-button__inner.is-round{padding:10px 20px}.custom-theme .el-radio-button--small .el-radio-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.custom-theme .el-radio-button--small .el-radio-button__inner.is-round{padding:9px 15px}.custom-theme .el-radio-button--mini .el-radio-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.custom-theme .el-radio-button--mini .el-radio-button__inner.is-round{padding:7px 15px}.custom-theme .el-radio-button:focus:not(.is-focus):not(:active){-webkit-box-shadow:0 0 2px 2px #262729;box-shadow:0 0 2px 2px #262729}.custom-theme .el-checkbox{color:#5a5e66;font-weight:500;font-size:14px;position:relative;cursor:pointer;display:inline-block;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-theme .el-checkbox.is-bordered{padding:9px 20px 9px 10px;border-radius:4px;border:1px solid #d8dce5}.custom-theme .el-checkbox.is-bordered.is-checked{border-color:#262729}.custom-theme .el-checkbox.is-bordered.is-disabled{border-color:#e6ebf5;cursor:not-allowed}.custom-theme .el-checkbox.is-bordered+.el-checkbox.is-bordered{margin-left:10px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium{padding:7px 20px 7px 10px;border-radius:4px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__label{line-height:17px;font-size:14px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__inner{height:14px;width:14px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small{padding:3px 15px 7px 10px;border-radius:3px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{line-height:15px;font-size:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner{height:12px;width:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner::after{height:6px;width:2px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini{padding:1px 15px 5px 10px;border-radius:3px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__label{line-height:12px;font-size:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner{height:12px;width:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner::after{height:6px;width:2px}.custom-theme .el-checkbox__input{white-space:nowrap;cursor:pointer;outline:0;display:inline-block;line-height:1;position:relative;vertical-align:middle}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5;cursor:not-allowed}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner::after{cursor:not-allowed;border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner+.el-checkbox__label{cursor:not-allowed}.custom-theme .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5}.custom-theme .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner::after{border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5}.custom-theme .el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner::before{background-color:#b4bccc;border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled+span.el-checkbox__label{color:#b4bccc;cursor:not-allowed}.custom-theme .el-checkbox__input.is-checked .el-checkbox__inner{background-color:#262729;border-color:#262729}.custom-theme .el-checkbox__input.is-checked .el-checkbox__inner::after{-webkit-transform:rotate(45deg) scaleY(1);transform:rotate(45deg) scaleY(1)}.custom-theme .el-checkbox__input.is-checked+.el-checkbox__label{color:#262729}.custom-theme .el-checkbox__input.is-focus .el-checkbox__inner{border-color:#262729}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#262729;border-color:#262729}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner::before{content:'';position:absolute;display:block;background-color:#fff;height:2px;-webkit-transform:scale(.5);transform:scale(.5);left:0;right:0;top:5px}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner::after{display:none}.custom-theme .el-checkbox__inner{display:inline-block;position:relative;border:1px solid #d8dce5;border-radius:2px;-webkit-box-sizing:border-box;box-sizing:border-box;width:14px;height:14px;background-color:#fff;z-index:1;-webkit-transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46);transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46)}.custom-theme .el-checkbox__inner:hover{border-color:#262729}.custom-theme .el-checkbox__inner::after{-webkit-box-sizing:content-box;box-sizing:content-box;content:\"\";border:1px solid #fff;border-left:0;border-top:0;height:7px;left:4px;position:absolute;top:1px;-webkit-transform:rotate(45deg) scaleY(0);transform:rotate(45deg) scaleY(0);width:3px;-webkit-transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms,-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;-webkit-transform-origin:center;transform-origin:center}.custom-theme .el-checkbox__original{opacity:0;outline:0;position:absolute;margin:0;width:0;height:0;left:-999px}.custom-theme .el-checkbox__label{display:inline-block;padding-left:10px;line-height:19px;font-size:14px}.custom-theme .el-checkbox+.el-checkbox{margin-left:30px}.custom-theme .el-checkbox-button{position:relative;display:inline-block}.custom-theme .el-checkbox-button__inner{display:inline-block;line-height:1;font-weight:500;white-space:nowrap;vertical-align:middle;cursor:pointer;background:#fff;border:1px solid #d8dce5;border-left:0;color:#5a5e66;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;position:relative;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:14px;border-radius:0}.custom-theme .el-checkbox-button__inner.is-round{padding:12px 20px}.custom-theme .el-checkbox-button__inner:hover{color:#262729}.custom-theme .el-checkbox-button__inner [class*=el-icon-]{line-height:.9}.custom-theme .el-checkbox-button__inner [class*=el-icon-]+span{margin-left:5px}.custom-theme .el-checkbox-button__original{opacity:0;outline:0;position:absolute;margin:0;left:-999px}.custom-theme .el-checkbox-button.is-checked .el-checkbox-button__inner{color:#fff;background-color:#262729;border-color:#262729;-webkit-box-shadow:-1px 0 0 0 #7d7d7f;box-shadow:-1px 0 0 0 #7d7d7f}.custom-theme .el-checkbox-button.is-disabled .el-checkbox-button__inner{color:#b4bccc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#e6ebf5;-webkit-box-shadow:none;box-shadow:none}.custom-theme .el-checkbox-button:first-child .el-checkbox-button__inner{border-left:1px solid #d8dce5;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.custom-theme .el-checkbox-button.is-focus .el-checkbox-button__inner{border-color:#262729}.custom-theme .el-checkbox-button:last-child .el-checkbox-button__inner{border-radius:0 4px 4px 0}.custom-theme .el-checkbox-button--medium .el-checkbox-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.custom-theme .el-checkbox-button--medium .el-checkbox-button__inner.is-round{padding:10px 20px}.custom-theme .el-checkbox-button--small .el-checkbox-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.custom-theme .el-checkbox-button--small .el-checkbox-button__inner.is-round{padding:9px 15px}.custom-theme .el-checkbox-button--mini .el-checkbox-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.custom-theme .el-checkbox-button--mini .el-checkbox-button__inner.is-round{padding:7px 15px}.custom-theme .el-checkbox-group{font-size:0}.custom-theme .el-switch{display:inline-block;position:relative;font-size:14px;line-height:20px;height:20px;vertical-align:middle}.custom-theme .el-switch.is-disabled .el-switch__core,.custom-theme .el-switch.is-disabled .el-switch__label{cursor:not-allowed}.custom-theme .el-switch__label{-webkit-transition:.2s;transition:.2s;height:20px;display:inline-block;font-size:14px;font-weight:500;cursor:pointer;vertical-align:middle;color:#2d2f33}.custom-theme .el-switch__label.is-active{color:#262729}.custom-theme .el-switch__label--left{margin-right:10px}.custom-theme .el-switch__label--right{margin-left:10px}.custom-theme .el-switch__label *{line-height:1;font-size:14px;display:inline-block}.custom-theme .el-switch__input{position:absolute;width:0;height:0;opacity:0;margin:0}.custom-theme .el-switch__input:focus~.el-switch__core{outline:1px solid #262729}.custom-theme .el-switch__core{margin:0;display:inline-block;position:relative;width:40px;height:20px;border:1px solid #d8dce5;outline:0;border-radius:10px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#d8dce5;cursor:pointer;-webkit-transition:border-color .3s,background-color .3s;transition:border-color .3s,background-color .3s;vertical-align:middle}.custom-theme .el-switch__core .el-switch__button{position:absolute;top:1px;left:1px;border-radius:100%;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;width:16px;height:16px;background-color:#fff}.custom-theme .el-switch.is-checked .el-switch__core{border-color:#262729;background-color:#262729}.custom-theme .el-switch.is-disabled{opacity:.6}.custom-theme .el-switch--wide .el-switch__label.el-switch__label--left span{left:10px}.custom-theme .el-switch--wide .el-switch__label.el-switch__label--right span{right:10px}.custom-theme .el-switch .label-fade-enter,.custom-theme .el-switch .label-fade-leave-active{opacity:0}.custom-theme .el-popper .popper__arrow,.custom-theme .el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.custom-theme .el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.custom-theme .el-popper .popper__arrow::after{content:\" \";border-width:6px}.custom-theme .el-popper[x-placement^=top]{margin-bottom:12px}.custom-theme .el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.custom-theme .el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.custom-theme .el-popper[x-placement^=bottom]{margin-top:12px}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.custom-theme .el-popper[x-placement^=right]{margin-left:12px}.custom-theme .el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.custom-theme .el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.custom-theme .el-popper[x-placement^=left]{margin-right:12px}.custom-theme .el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.custom-theme .el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.custom-theme .el-select-dropdown{position:absolute;z-index:1001;border:solid 1px #dfe4ed;border-radius:4px;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:5px 0}.custom-theme .el-select-dropdown.is-multiple .el-select-dropdown__item.selected{color:#262729;background-color:#fff}.custom-theme .el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover{background-color:#f5f7fa}.custom-theme .el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after{position:absolute;right:20px;font-family:element-icons;content:\"\\E611\";font-size:12px;font-weight:700;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.custom-theme .el-select-dropdown .el-scrollbar.is-empty .el-select-dropdown__list{padding:0}.custom-theme .el-select-dropdown .popper__arrow{-webkit-transform:translateX(-400%);transform:translateX(-400%)}.custom-theme .el-select-dropdown.is-arrow-fixed .popper__arrow{-webkit-transform:translateX(-200%);transform:translateX(-200%)}.custom-theme .el-select-dropdown__empty{padding:10px 0;margin:0;text-align:center;color:#999;font-size:14px}.custom-theme .el-select-dropdown__wrap{max-height:274px}.custom-theme .el-select-dropdown__list{list-style:none;padding:6px 0;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.custom-theme .el-input::-webkit-scrollbar{z-index:11;width:6px}.custom-theme .el-input::-webkit-scrollbar:horizontal{height:6px}.custom-theme .el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.custom-theme .el-input::-webkit-scrollbar-corner{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.custom-theme .el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.custom-theme .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input__inner:hover{border-color:#b4bccc}.custom-theme .el-input__inner:focus{outline:0;border-color:#262729}.custom-theme .el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.custom-theme .el-input__suffix-inner{pointer-events:all}.custom-theme .el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s}.custom-theme .el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.custom-theme .el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.custom-theme .el-input__validateIcon{pointer-events:none}.custom-theme .el-input.is-active .el-input__inner{outline:0;border-color:#262729}.custom-theme .el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__icon{cursor:not-allowed}.custom-theme .el-input--suffix .el-input__inner{padding-right:30px}.custom-theme .el-input--prefix .el-input__inner{padding-left:30px}.custom-theme .el-input--medium{font-size:14px}.custom-theme .el-input--medium .el-input__inner{height:36px}.custom-theme .el-input--medium .el-input__icon{line-height:36px}.custom-theme .el-input--small{font-size:13px}.custom-theme .el-input--small .el-input__inner{height:32px}.custom-theme .el-input--small .el-input__icon{line-height:32px}.custom-theme .el-input--mini{font-size:12px}.custom-theme .el-input--mini .el-input__inner{height:28px}.custom-theme .el-input--mini .el-input__icon{line-height:28px}.custom-theme .el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.custom-theme .el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.custom-theme .el-input-group__append,.custom-theme .el-input-group__prepend{background-color:#f5f7fa;color:#0a76a4;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.custom-theme .el-input-group__append:focus,.custom-theme .el-input-group__prepend:focus{outline:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-select,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-select{display:inline-block;margin:-20px}.custom-theme .el-input-group__append button.el-button,.custom-theme .el-input-group__append div.el-select .el-input__inner,.custom-theme .el-input-group__append div.el-select:hover .el-input__inner,.custom-theme .el-input-group__prepend button.el-button,.custom-theme .el-input-group__prepend div.el-select .el-input__inner,.custom-theme .el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-input,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-input{font-size:inherit}.custom-theme .el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-textarea{display:inline-block;width:100%;vertical-align:bottom}.custom-theme .el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:hover{border-color:#b4bccc}.custom-theme .el-textarea__inner:focus{outline:0;border-color:#262729}.custom-theme .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-tag{background-color:rgba(38,39,41,.1);display:inline-block;padding:0 10px;height:32px;line-height:30px;font-size:12px;color:#262729;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid rgba(38,39,41,.2);white-space:nowrap}.custom-theme .el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:18px;width:18px;line-height:18px;vertical-align:middle;top:-1px;right:-5px;color:#262729}.custom-theme .el-tag .el-icon-close::before{display:block}.custom-theme .el-tag .el-icon-close:hover{background-color:#262729;color:#fff}.custom-theme .el-tag--info{background-color:rgba(10,118,164,.1);border-color:rgba(10,118,164,.2);color:#0a76a4}.custom-theme .el-tag--info.is-hit{border-color:#0a76a4}.custom-theme .el-tag--info .el-tag__close{color:#0a76a4}.custom-theme .el-tag--info .el-tag__close:hover{background-color:#0a76a4;color:#fff}.custom-theme .el-tag--success{background-color:rgba(64,145,103,.1);border-color:rgba(64,145,103,.2);color:#409167}.custom-theme .el-tag--success.is-hit{border-color:#409167}.custom-theme .el-tag--success .el-tag__close{color:#409167}.custom-theme .el-tag--success .el-tag__close:hover{background-color:#409167;color:#fff}.custom-theme .el-tag--warning{background-color:rgba(157,164,8,.1);border-color:rgba(157,164,8,.2);color:#9da408}.custom-theme .el-tag--warning.is-hit{border-color:#9da408}.custom-theme .el-tag--warning .el-tag__close{color:#9da408}.custom-theme .el-tag--warning .el-tag__close:hover{background-color:#9da408;color:#fff}.custom-theme .el-tag--danger{background-color:rgba(179,69,14,.1);border-color:rgba(179,69,14,.2);color:#b3450e}.custom-theme .el-tag--danger.is-hit{border-color:#b3450e}.custom-theme .el-tag--danger .el-tag__close{color:#b3450e}.custom-theme .el-tag--danger .el-tag__close:hover{background-color:#b3450e;color:#fff}.custom-theme .el-tag--medium{height:28px;line-height:26px}.custom-theme .el-tag--medium .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-tag--small{height:24px;padding:0 8px;line-height:22px}.custom-theme .el-tag--small .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-tag--mini{height:20px;padding:0 5px;line-height:19px}.custom-theme .el-tag--mini .el-icon-close{margin-left:-3px;-webkit-transform:scale(.7);transform:scale(.7)}.custom-theme .el-select-dropdown__item{font-size:14px;padding:0 20px;position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#5a5e66;height:34px;line-height:34px;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:pointer}.custom-theme .el-select-dropdown__item.is-disabled{color:#b4bccc;cursor:not-allowed}.custom-theme .el-select-dropdown__item.is-disabled:hover{background-color:#fff}.custom-theme .el-select-dropdown__item.hover,.custom-theme .el-select-dropdown__item:hover{background-color:#f5f7fa}.custom-theme .el-select-dropdown__item.selected{color:#262729;font-weight:700}.custom-theme .el-select-dropdown__item span{line-height:34px!important}.custom-theme .el-select-group{margin:0;padding:0}.custom-theme .el-select-group__wrap{position:relative;list-style:none;margin:0;padding:0}.custom-theme .el-select-group__wrap:not(:last-of-type){padding-bottom:24px}.custom-theme .el-select-group__wrap:not(:last-of-type)::after{content:'';position:absolute;display:block;left:20px;right:20px;bottom:12px;height:1px;background:#dfe4ed}.custom-theme .el-select-group__title{padding-left:20px;font-size:12px;color:#0a76a4;line-height:30px}.custom-theme .el-select-group .el-select-dropdown__item{padding-left:20px}.custom-theme .el-scrollbar{overflow:hidden;position:relative}.custom-theme .el-scrollbar:active>.el-scrollbar__bar,.custom-theme .el-scrollbar:focus>.el-scrollbar__bar,.custom-theme .el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity 340ms ease-out;transition:opacity 340ms ease-out}.custom-theme .el-scrollbar__wrap{overflow:scroll;height:100%}.custom-theme .el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.custom-theme .el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(135,141,153,.3);-webkit-transition:.3s background-color;transition:.3s background-color}.custom-theme .el-scrollbar__thumb:hover{background-color:rgba(135,141,153,.5)}.custom-theme .el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity 120ms ease-out;transition:opacity 120ms ease-out}.custom-theme .el-scrollbar__bar.is-vertical{width:6px;top:2px}.custom-theme .el-scrollbar__bar.is-vertical>div{width:100%}.custom-theme .el-scrollbar__bar.is-horizontal{height:6px;left:2px}.custom-theme .el-scrollbar__bar.is-horizontal>div{height:100%}.custom-theme .el-select{display:inline-block;position:relative}.custom-theme .el-select:hover .el-input__inner{border-color:#b4bccc}.custom-theme .el-select .el-input__inner{cursor:pointer;padding-right:35px}.custom-theme .el-select .el-input__inner:focus{border-color:#262729}.custom-theme .el-select .el-input .el-select__caret{color:#b4bccc;font-size:14px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg);line-height:16px;cursor:pointer}.custom-theme .el-select .el-input .el-select__caret.is-reverse{-webkit-transform:rotateZ(0);transform:rotateZ(0)}.custom-theme .el-select .el-input .el-select__caret.is-show-close{font-size:14px;text-align:center;-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg);border-radius:100%;color:#b4bccc;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-select .el-input .el-select__caret.is-show-close:hover{color:#878d99}.custom-theme .el-select .el-input.is-disabled .el-input__inner{cursor:not-allowed}.custom-theme .el-select .el-input.is-disabled .el-input__inner:hover{border-color:#dfe4ed}.custom-theme .el-select>.el-input{display:block}.custom-theme .el-select__input{border:none;outline:0;padding:0;margin-left:15px;color:#666;font-size:14px;vertical-align:baseline;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:28px;background-color:transparent}.custom-theme .el-select__input.is-mini{height:14px}.custom-theme .el-select__close{cursor:pointer;position:absolute;top:8px;z-index:1000;right:25px;color:#b4bccc;line-height:18px;font-size:14px}.custom-theme .el-select__close:hover{color:#878d99}.custom-theme .el-select__tags{position:absolute;line-height:normal;white-space:normal;z-index:1;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.custom-theme .el-select .el-tag__close{margin-top:-2px}.custom-theme .el-select .el-tag{-webkit-box-sizing:border-box;box-sizing:border-box;border-color:transparent;margin:3px 0 3px 6px;background-color:#f0f2f5}.custom-theme .el-select .el-tag__close.el-icon-close{background-color:#b4bccc;right:-7px;color:#fff}.custom-theme .el-select .el-tag__close.el-icon-close:hover{background-color:#878d99}.custom-theme .el-select .el-tag__close.el-icon-close::before{display:block;-webkit-transform:translate(0,.5px);transform:translate(0,.5px)}.custom-theme .el-select__tag{display:inline-block;height:24px;line-height:24px;font-size:14px;border-radius:4px;color:#fff;background-color:#262729}.custom-theme .el-select__tag .el-icon-close{font-size:14px}.custom-theme .el-button{display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;background:#fff;border:1px solid #d8dce5;border-color:#d8dce5;color:#5a5e66;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:.1s;transition:.1s;font-weight:500;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:14px;border-radius:4px}.custom-theme .el-button+.el-button{margin-left:10px}.custom-theme .el-button.is-round{padding:12px 20px}.custom-theme .el-button:focus,.custom-theme .el-button:hover{color:#262729;border-color:#bebebf;background-color:#e9e9ea}.custom-theme .el-button:active{color:#222325;border-color:#222325;outline:0}.custom-theme .el-button::-moz-focus-inner{border:0}.custom-theme .el-button [class*=el-icon-]+span{margin-left:5px}.custom-theme .el-button.is-plain:focus,.custom-theme .el-button.is-plain:hover{background:#fff;border-color:#262729;color:#262729}.custom-theme .el-button.is-plain:active{background:#fff;border-color:#222325;color:#222325;outline:0}.custom-theme .el-button.is-active{color:#222325;border-color:#222325}.custom-theme .el-button.is-disabled,.custom-theme .el-button.is-disabled:focus,.custom-theme .el-button.is-disabled:hover{color:#b4bccc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#e6ebf5}.custom-theme .el-button.is-disabled.el-button--text{background-color:transparent}.custom-theme .el-button.is-disabled.is-plain,.custom-theme .el-button.is-disabled.is-plain:focus,.custom-theme .el-button.is-disabled.is-plain:hover{background-color:#fff;border-color:#e6ebf5;color:#b4bccc}.custom-theme .el-button.is-loading{position:relative;pointer-events:none}.custom-theme .el-button.is-loading:before{pointer-events:none;content:'';position:absolute;left:-1px;top:-1px;right:-1px;bottom:-1px;border-radius:inherit;background-color:rgba(255,255,255,.35)}.custom-theme .el-button.is-round{border-radius:20px;padding:12px 23px}.custom-theme .el-button--primary{color:#fff;background-color:#262729;border-color:#262729}.custom-theme .el-button--primary:focus,.custom-theme .el-button--primary:hover{background:#515254;border-color:#515254;color:#fff}.custom-theme .el-button--primary:active{background:#222325;border-color:#222325;color:#fff;outline:0}.custom-theme .el-button--primary.is-active{background:#222325;border-color:#222325;color:#fff}.custom-theme .el-button--primary.is-disabled,.custom-theme .el-button--primary.is-disabled:active,.custom-theme .el-button--primary.is-disabled:focus,.custom-theme .el-button--primary.is-disabled:hover{color:#fff;background-color:#939394;border-color:#939394}.custom-theme .el-button--primary.is-plain{color:#262729;background:#e9e9ea;border-color:#a8a9a9}.custom-theme .el-button--primary.is-plain:focus,.custom-theme .el-button--primary.is-plain:hover{background:#262729;border-color:#262729;color:#fff}.custom-theme .el-button--primary.is-plain:active{background:#222325;border-color:#222325;color:#fff;outline:0}.custom-theme .el-button--primary.is-plain.is-disabled,.custom-theme .el-button--primary.is-plain.is-disabled:active,.custom-theme .el-button--primary.is-plain.is-disabled:focus,.custom-theme .el-button--primary.is-plain.is-disabled:hover{color:#7d7d7f;background-color:#e9e9ea;border-color:#d4d4d4}.custom-theme .el-button--success{color:#fff;background-color:#409167;border-color:#409167}.custom-theme .el-button--success:focus,.custom-theme .el-button--success:hover{background:#66a785;border-color:#66a785;color:#fff}.custom-theme .el-button--success:active{background:#3a835d;border-color:#3a835d;color:#fff;outline:0}.custom-theme .el-button--success.is-active{background:#3a835d;border-color:#3a835d;color:#fff}.custom-theme .el-button--success.is-disabled,.custom-theme .el-button--success.is-disabled:active,.custom-theme .el-button--success.is-disabled:focus,.custom-theme .el-button--success.is-disabled:hover{color:#fff;background-color:#a0c8b3;border-color:#a0c8b3}.custom-theme .el-button--success.is-plain{color:#409167;background:#ecf4f0;border-color:#b3d3c2}.custom-theme .el-button--success.is-plain:focus,.custom-theme .el-button--success.is-plain:hover{background:#409167;border-color:#409167;color:#fff}.custom-theme .el-button--success.is-plain:active{background:#3a835d;border-color:#3a835d;color:#fff;outline:0}.custom-theme .el-button--success.is-plain.is-disabled,.custom-theme .el-button--success.is-plain.is-disabled:active,.custom-theme .el-button--success.is-plain.is-disabled:focus,.custom-theme .el-button--success.is-plain.is-disabled:hover{color:#8cbda4;background-color:#ecf4f0;border-color:#d9e9e1}.custom-theme .el-button--warning{color:#fff;background-color:#9da408;border-color:#9da408}.custom-theme .el-button--warning:focus,.custom-theme .el-button--warning:hover{background:#b1b639;border-color:#b1b639;color:#fff}.custom-theme .el-button--warning:active{background:#8d9407;border-color:#8d9407;color:#fff;outline:0}.custom-theme .el-button--warning.is-active{background:#8d9407;border-color:#8d9407;color:#fff}.custom-theme .el-button--warning.is-disabled,.custom-theme .el-button--warning.is-disabled:active,.custom-theme .el-button--warning.is-disabled:focus,.custom-theme .el-button--warning.is-disabled:hover{color:#fff;background-color:#ced284;border-color:#ced284}.custom-theme .el-button--warning.is-plain{color:#9da408;background:#f5f6e6;border-color:#d8db9c}.custom-theme .el-button--warning.is-plain:focus,.custom-theme .el-button--warning.is-plain:hover{background:#9da408;border-color:#9da408;color:#fff}.custom-theme .el-button--warning.is-plain:active{background:#8d9407;border-color:#8d9407;color:#fff;outline:0}.custom-theme .el-button--warning.is-plain.is-disabled,.custom-theme .el-button--warning.is-plain.is-disabled:active,.custom-theme .el-button--warning.is-plain.is-disabled:focus,.custom-theme .el-button--warning.is-plain.is-disabled:hover{color:#c4c86b;background-color:#f5f6e6;border-color:#ebedce}.custom-theme .el-button--danger{color:#fff;background-color:#b3450e;border-color:#b3450e}.custom-theme .el-button--danger:focus,.custom-theme .el-button--danger:hover{background:#c26a3e;border-color:#c26a3e;color:#fff}.custom-theme .el-button--danger:active{background:#a13e0d;border-color:#a13e0d;color:#fff;outline:0}.custom-theme .el-button--danger.is-active{background:#a13e0d;border-color:#a13e0d;color:#fff}.custom-theme .el-button--danger.is-disabled,.custom-theme .el-button--danger.is-disabled:active,.custom-theme .el-button--danger.is-disabled:focus,.custom-theme .el-button--danger.is-disabled:hover{color:#fff;background-color:#d9a287;border-color:#d9a287}.custom-theme .el-button--danger.is-plain{color:#b3450e;background:#f7ece7;border-color:#e1b59f}.custom-theme .el-button--danger.is-plain:focus,.custom-theme .el-button--danger.is-plain:hover{background:#b3450e;border-color:#b3450e;color:#fff}.custom-theme .el-button--danger.is-plain:active{background:#a13e0d;border-color:#a13e0d;color:#fff;outline:0}.custom-theme .el-button--danger.is-plain.is-disabled,.custom-theme .el-button--danger.is-plain.is-disabled:active,.custom-theme .el-button--danger.is-plain.is-disabled:focus,.custom-theme .el-button--danger.is-plain.is-disabled:hover{color:#d18f6e;background-color:#f7ece7;border-color:#f0dacf}.custom-theme .el-button--info{color:#fff;background-color:#0a76a4;border-color:#0a76a4}.custom-theme .el-button--info:focus,.custom-theme .el-button--info:hover{background:#3b91b6;border-color:#3b91b6;color:#fff}.custom-theme .el-button--info:active{background:#096a94;border-color:#096a94;color:#fff;outline:0}.custom-theme .el-button--info.is-active{background:#096a94;border-color:#096a94;color:#fff}.custom-theme .el-button--info.is-disabled,.custom-theme .el-button--info.is-disabled:active,.custom-theme .el-button--info.is-disabled:focus,.custom-theme .el-button--info.is-disabled:hover{color:#fff;background-color:#85bbd2;border-color:#85bbd2}.custom-theme .el-button--info.is-plain{color:#0a76a4;background:#e7f1f6;border-color:#9dc8db}.custom-theme .el-button--info.is-plain:focus,.custom-theme .el-button--info.is-plain:hover{background:#0a76a4;border-color:#0a76a4;color:#fff}.custom-theme .el-button--info.is-plain:active{background:#096a94;border-color:#096a94;color:#fff;outline:0}.custom-theme .el-button--info.is-plain.is-disabled,.custom-theme .el-button--info.is-plain.is-disabled:active,.custom-theme .el-button--info.is-plain.is-disabled:focus,.custom-theme .el-button--info.is-plain.is-disabled:hover{color:#6cadc8;background-color:#e7f1f6;border-color:#cee4ed}.custom-theme .el-button--medium{padding:10px 20px;font-size:14px;border-radius:4px}.custom-theme .el-button--medium.is-round{padding:10px 20px}.custom-theme .el-button--small{padding:9px 15px;font-size:12px;border-radius:3px}.custom-theme .el-button--small.is-round{padding:9px 15px}.custom-theme .el-button--mini{padding:7px 15px;font-size:12px;border-radius:3px}.custom-theme .el-button--mini.is-round{padding:7px 15px}.custom-theme .el-button--text{border:none;color:#262729;background:0 0;padding-left:0;padding-right:0}.custom-theme .el-button--text:focus,.custom-theme .el-button--text:hover{color:#515254;border-color:transparent;background-color:transparent}.custom-theme .el-button--text:active{color:#222325;border-color:transparent;background-color:transparent}.custom-theme .el-button-group{display:inline-block;vertical-align:middle}.custom-theme .el-button-group::after,.custom-theme .el-button-group::before{display:table;content:\"\"}.custom-theme .el-button-group::after{clear:both}.custom-theme .el-button-group .el-button{float:left;position:relative}.custom-theme .el-button-group .el-button+.el-button{margin-left:0}.custom-theme .el-button-group .el-button:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-button-group .el-button:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-button-group .el-button:not(:first-child):not(:last-child){border-radius:0}.custom-theme .el-button-group .el-button:not(:last-child){margin-right:-1px}.custom-theme .el-button-group .el-button:active,.custom-theme .el-button-group .el-button:focus,.custom-theme .el-button-group .el-button:hover{z-index:1}.custom-theme .el-button-group .el-button.is-active{z-index:1}.custom-theme .el-button-group .el-button--primary:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--primary:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--primary:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-checkbox{color:#5a5e66;font-weight:500;font-size:14px;position:relative;cursor:pointer;display:inline-block;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-theme .el-checkbox.is-bordered{padding:9px 20px 9px 10px;border-radius:4px;border:1px solid #d8dce5}.custom-theme .el-checkbox.is-bordered.is-checked{border-color:#262729}.custom-theme .el-checkbox.is-bordered.is-disabled{border-color:#e6ebf5;cursor:not-allowed}.custom-theme .el-checkbox.is-bordered+.el-checkbox.is-bordered{margin-left:10px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium{padding:7px 20px 7px 10px;border-radius:4px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__label{line-height:17px;font-size:14px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__inner{height:14px;width:14px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small{padding:3px 15px 7px 10px;border-radius:3px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{line-height:15px;font-size:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner{height:12px;width:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner::after{height:6px;width:2px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini{padding:1px 15px 5px 10px;border-radius:3px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__label{line-height:12px;font-size:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner{height:12px;width:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner::after{height:6px;width:2px}.custom-theme .el-checkbox__input{white-space:nowrap;cursor:pointer;outline:0;display:inline-block;line-height:1;position:relative;vertical-align:middle}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5;cursor:not-allowed}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner::after{cursor:not-allowed;border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner+.el-checkbox__label{cursor:not-allowed}.custom-theme .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5}.custom-theme .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner::after{border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5}.custom-theme .el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner::before{background-color:#b4bccc;border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled+span.el-checkbox__label{color:#b4bccc;cursor:not-allowed}.custom-theme .el-checkbox__input.is-checked .el-checkbox__inner{background-color:#262729;border-color:#262729}.custom-theme .el-checkbox__input.is-checked .el-checkbox__inner::after{-webkit-transform:rotate(45deg) scaleY(1);transform:rotate(45deg) scaleY(1)}.custom-theme .el-checkbox__input.is-checked+.el-checkbox__label{color:#262729}.custom-theme .el-checkbox__input.is-focus .el-checkbox__inner{border-color:#262729}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#262729;border-color:#262729}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner::before{content:'';position:absolute;display:block;background-color:#fff;height:2px;-webkit-transform:scale(.5);transform:scale(.5);left:0;right:0;top:5px}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner::after{display:none}.custom-theme .el-checkbox__inner{display:inline-block;position:relative;border:1px solid #d8dce5;border-radius:2px;-webkit-box-sizing:border-box;box-sizing:border-box;width:14px;height:14px;background-color:#fff;z-index:1;-webkit-transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46);transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46)}.custom-theme .el-checkbox__inner:hover{border-color:#262729}.custom-theme .el-checkbox__inner::after{-webkit-box-sizing:content-box;box-sizing:content-box;content:\"\";border:1px solid #fff;border-left:0;border-top:0;height:7px;left:4px;position:absolute;top:1px;-webkit-transform:rotate(45deg) scaleY(0);transform:rotate(45deg) scaleY(0);width:3px;-webkit-transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms,-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;-webkit-transform-origin:center;transform-origin:center}.custom-theme .el-checkbox__original{opacity:0;outline:0;position:absolute;margin:0;width:0;height:0;left:-999px}.custom-theme .el-checkbox__label{display:inline-block;padding-left:10px;line-height:19px;font-size:14px}.custom-theme .el-checkbox+.el-checkbox{margin-left:30px}.custom-theme .el-checkbox-button{position:relative;display:inline-block}.custom-theme .el-checkbox-button__inner{display:inline-block;line-height:1;font-weight:500;white-space:nowrap;vertical-align:middle;cursor:pointer;background:#fff;border:1px solid #d8dce5;border-left:0;color:#5a5e66;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;position:relative;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:14px;border-radius:0}.custom-theme .el-checkbox-button__inner.is-round{padding:12px 20px}.custom-theme .el-checkbox-button__inner:hover{color:#262729}.custom-theme .el-checkbox-button__inner [class*=el-icon-]{line-height:.9}.custom-theme .el-checkbox-button__inner [class*=el-icon-]+span{margin-left:5px}.custom-theme .el-checkbox-button__original{opacity:0;outline:0;position:absolute;margin:0;left:-999px}.custom-theme .el-checkbox-button.is-checked .el-checkbox-button__inner{color:#fff;background-color:#262729;border-color:#262729;-webkit-box-shadow:-1px 0 0 0 #7d7d7f;box-shadow:-1px 0 0 0 #7d7d7f}.custom-theme .el-checkbox-button.is-disabled .el-checkbox-button__inner{color:#b4bccc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#e6ebf5;-webkit-box-shadow:none;box-shadow:none}.custom-theme .el-checkbox-button:first-child .el-checkbox-button__inner{border-left:1px solid #d8dce5;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.custom-theme .el-checkbox-button.is-focus .el-checkbox-button__inner{border-color:#262729}.custom-theme .el-checkbox-button:last-child .el-checkbox-button__inner{border-radius:0 4px 4px 0}.custom-theme .el-checkbox-button--medium .el-checkbox-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.custom-theme .el-checkbox-button--medium .el-checkbox-button__inner.is-round{padding:10px 20px}.custom-theme .el-checkbox-button--small .el-checkbox-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.custom-theme .el-checkbox-button--small .el-checkbox-button__inner.is-round{padding:9px 15px}.custom-theme .el-checkbox-button--mini .el-checkbox-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.custom-theme .el-checkbox-button--mini .el-checkbox-button__inner.is-round{padding:7px 15px}.custom-theme .el-checkbox-group{font-size:0}.custom-theme .el-tag{background-color:rgba(38,39,41,.1);display:inline-block;padding:0 10px;height:32px;line-height:30px;font-size:12px;color:#262729;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid rgba(38,39,41,.2);white-space:nowrap}.custom-theme .el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:18px;width:18px;line-height:18px;vertical-align:middle;top:-1px;right:-5px;color:#262729}.custom-theme .el-tag .el-icon-close::before{display:block}.custom-theme .el-tag .el-icon-close:hover{background-color:#262729;color:#fff}.custom-theme .el-tag--info{background-color:rgba(10,118,164,.1);border-color:rgba(10,118,164,.2);color:#0a76a4}.custom-theme .el-tag--info.is-hit{border-color:#0a76a4}.custom-theme .el-tag--info .el-tag__close{color:#0a76a4}.custom-theme .el-tag--info .el-tag__close:hover{background-color:#0a76a4;color:#fff}.custom-theme .el-tag--success{background-color:rgba(64,145,103,.1);border-color:rgba(64,145,103,.2);color:#409167}.custom-theme .el-tag--success.is-hit{border-color:#409167}.custom-theme .el-tag--success .el-tag__close{color:#409167}.custom-theme .el-tag--success .el-tag__close:hover{background-color:#409167;color:#fff}.custom-theme .el-tag--warning{background-color:rgba(157,164,8,.1);border-color:rgba(157,164,8,.2);color:#9da408}.custom-theme .el-tag--warning.is-hit{border-color:#9da408}.custom-theme .el-tag--warning .el-tag__close{color:#9da408}.custom-theme .el-tag--warning .el-tag__close:hover{background-color:#9da408;color:#fff}.custom-theme .el-tag--danger{background-color:rgba(179,69,14,.1);border-color:rgba(179,69,14,.2);color:#b3450e}.custom-theme .el-tag--danger.is-hit{border-color:#b3450e}.custom-theme .el-tag--danger .el-tag__close{color:#b3450e}.custom-theme .el-tag--danger .el-tag__close:hover{background-color:#b3450e;color:#fff}.custom-theme .el-tag--medium{height:28px;line-height:26px}.custom-theme .el-tag--medium .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-tag--small{height:24px;padding:0 8px;line-height:22px}.custom-theme .el-tag--small .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-tag--mini{height:20px;padding:0 5px;line-height:19px}.custom-theme .el-tag--mini .el-icon-close{margin-left:-3px;-webkit-transform:scale(.7);transform:scale(.7)}.custom-theme .el-table{position:relative;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-box-flex:1;-ms-flex:1;flex:1;width:100%;max-width:100%;background-color:#fff;font-size:14px;color:#5a5e66}.custom-theme .el-table__empty-block{position:relative;min-height:60px;text-align:center;width:100%;height:100%}.custom-theme .el-table__empty-text{position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:color(#262729 s(16%) l(44%))}.custom-theme .el-table__expand-column .cell{padding:0;text-align:center}.custom-theme .el-table__expand-icon{position:relative;cursor:pointer;color:#666;font-size:12px;-webkit-transition:-webkit-transform .2s ease-in-out;transition:-webkit-transform .2s ease-in-out;transition:transform .2s ease-in-out;transition:transform .2s ease-in-out,-webkit-transform .2s ease-in-out;height:20px}.custom-theme .el-table__expand-icon--expanded{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.custom-theme .el-table__expand-icon>.el-icon{position:absolute;left:50%;top:50%;margin-left:-5px;margin-top:-5px}.custom-theme .el-table__expanded-cell{background-color:#fff}.custom-theme .el-table__expanded-cell[class*=cell]{padding:20px 50px}.custom-theme .el-table__expanded-cell:hover{background-color:#f5f7fa!important}.custom-theme .el-table--fit{border-right:0;border-bottom:0}.custom-theme .el-table--fit td.gutter,.custom-theme .el-table--fit th.gutter{border-right-width:1px}.custom-theme .el-table thead{color:#878d99;font-weight:500}.custom-theme .el-table thead.is-group th{background:#f5f7fa}.custom-theme .el-table td,.custom-theme .el-table th{padding:12px 0;min-width:0;-webkit-box-sizing:border-box;box-sizing:border-box;text-overflow:ellipsis;vertical-align:middle;position:relative}.custom-theme .el-table td.is-center,.custom-theme .el-table th.is-center{text-align:center}.custom-theme .el-table td.is-left,.custom-theme .el-table th.is-left{text-align:left}.custom-theme .el-table td.is-right,.custom-theme .el-table th.is-right{text-align:right}.custom-theme .el-table td.gutter,.custom-theme .el-table th.gutter{width:15px;border-right-width:0;border-bottom-width:0;padding:0}.custom-theme .el-table td.is-hidden>*,.custom-theme .el-table th.is-hidden>*{visibility:hidden}.custom-theme .el-table--medium td,.custom-theme .el-table--medium th{padding:10px 0}.custom-theme .el-table--small{font-size:12px}.custom-theme .el-table--small td,.custom-theme .el-table--small th{padding:8px 0}.custom-theme .el-table--mini{font-size:12px}.custom-theme .el-table--mini td,.custom-theme .el-table--mini th{padding:6px 0}.custom-theme .el-table tr{background-color:#fff}.custom-theme .el-table tr input[type=checkbox]{margin:0}.custom-theme .el-table td,.custom-theme .el-table th.is-leaf{border-bottom:1px solid #e6ebf5}.custom-theme .el-table th.is-sortable{cursor:pointer}.custom-theme .el-table th{white-space:nowrap;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-align:left}.custom-theme .el-table th div{display:inline-block;padding-left:10px;padding-right:10px;line-height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.custom-theme .el-table th>.cell{position:relative;word-wrap:normal;text-overflow:ellipsis;display:inline-block;vertical-align:middle;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-table th>.cell.highlight{color:#262729}.custom-theme .el-table th.required>div::before{display:inline-block;content:\"\";width:8px;height:8px;border-radius:50%;background:#ff4d51;margin-right:5px;vertical-align:middle}.custom-theme .el-table td div{-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-table td.gutter{width:0}.custom-theme .el-table .cell{-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;text-overflow:ellipsis;white-space:normal;word-break:break-all;line-height:23px;padding-left:10px;padding-right:10px}.custom-theme .el-table .cell.el-tooltip{white-space:nowrap;min-width:50px}.custom-theme .el-table td:first-child .cell,.custom-theme .el-table th:first-child .cell{padding-left:0}.custom-theme .el-table--border,.custom-theme .el-table--group{border:1px solid #e6ebf5}.custom-theme .el-table--border::after,.custom-theme .el-table--group::after,.custom-theme .el-table::before{content:'';position:absolute;background-color:#e6ebf5;z-index:1}.custom-theme .el-table--border::after,.custom-theme .el-table--group::after{top:0;right:0;width:1px;height:100%}.custom-theme .el-table::before{left:0;bottom:0;width:100%;height:1px}.custom-theme .el-table--border{border-right:none;border-bottom:none}.custom-theme .el-table--border td,.custom-theme .el-table--border th{border-right:1px solid #e6ebf5}.custom-theme .el-table--border td:first-child .cell,.custom-theme .el-table--border th:first-child .cell{padding-left:10px}.custom-theme .el-table--border .has-gutter td:nth-last-of-type(2),.custom-theme .el-table--border .has-gutter th:nth-last-of-type(2){border-right:none}.custom-theme .el-table--border th.gutter:last-of-type{border-bottom:1px solid #e6ebf5;border-bottom-width:1px}.custom-theme .el-table--border th{border-bottom:1px solid #e6ebf5}.custom-theme .el-table--hidden{visibility:hidden}.custom-theme .el-table__fixed,.custom-theme .el-table__fixed-right{position:absolute;top:0;left:0;overflow-x:hidden;-webkit-box-shadow:0 0 10px rgba(0,0,0,.12);box-shadow:0 0 10px rgba(0,0,0,.12)}.custom-theme .el-table__fixed-right::before,.custom-theme .el-table__fixed::before{content:'';position:absolute;left:0;bottom:0;width:100%;height:1px;background-color:#e6ebf5;z-index:4}.custom-theme .el-table__fixed-right-patch{position:absolute;top:-1px;right:0;background-color:#fff;border-bottom:1px solid #e6ebf5}.custom-theme .el-table__fixed-right{top:0;left:auto;right:0}.custom-theme .el-table__fixed-right .el-table__fixed-body-wrapper,.custom-theme .el-table__fixed-right .el-table__fixed-footer-wrapper,.custom-theme .el-table__fixed-right .el-table__fixed-header-wrapper{left:auto;right:0}.custom-theme .el-table__fixed-header-wrapper{position:absolute;left:0;top:0;z-index:3}.custom-theme .el-table__fixed-footer-wrapper{position:absolute;left:0;bottom:0;z-index:3}.custom-theme .el-table__fixed-footer-wrapper tbody td{border-top:1px solid #e6ebf5;background-color:#f5f7fa;color:#5a5e66}.custom-theme .el-table__fixed-body-wrapper{position:absolute;left:0;top:37px;overflow:hidden;z-index:3}.custom-theme .el-table__body-wrapper,.custom-theme .el-table__footer-wrapper,.custom-theme .el-table__header-wrapper{width:100%}.custom-theme .el-table__footer-wrapper{margin-top:-1px}.custom-theme .el-table__footer-wrapper td{border-top:1px solid #e6ebf5}.custom-theme .el-table__body,.custom-theme .el-table__footer,.custom-theme .el-table__header{table-layout:fixed}.custom-theme .el-table__footer-wrapper,.custom-theme .el-table__header-wrapper{overflow:hidden}.custom-theme .el-table__footer-wrapper tbody td,.custom-theme .el-table__header-wrapper tbody td{background-color:#f5f7fa;color:#5a5e66}.custom-theme .el-table__body-wrapper{overflow:auto;position:relative}.custom-theme .el-table__body-wrapper.is-scroll-none~.el-table__fixed,.custom-theme .el-table__body-wrapper.is-scroll-none~.el-table__fixed-right{-webkit-box-shadow:none;box-shadow:none}.custom-theme .el-table__body-wrapper.is-scroll-left~.el-table__fixed{-webkit-box-shadow:none;box-shadow:none}.custom-theme .el-table__body-wrapper.is-scroll-right~.el-table__fixed-right{-webkit-box-shadow:none;box-shadow:none}.custom-theme .el-table__body-wrapper .el-table--border.is-scroll-right~.el-table__fixed-right{border-left:1px solid #e6ebf5}.custom-theme .el-table__body-wrapper .el-table--border.is-scroll-left~.el-table__fixed{border-right:1px solid #e6ebf5}.custom-theme .el-table .caret-wrapper{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:13px;width:24px;cursor:pointer;overflow:initial}.custom-theme .el-table .sort-caret{color:#0a76a4;width:14px;overflow:hidden;font-size:13px}.custom-theme .el-table .ascending .sort-caret.ascending{color:#262729}.custom-theme .el-table .descending .sort-caret.descending{color:#262729}.custom-theme .el-table .hidden-columns{visibility:hidden;position:absolute;z-index:-1}.custom-theme .el-table--striped .el-table__body tr.el-table__row--striped td{background:#fafafa}.custom-theme .el-table--striped .el-table__body tr.el-table__row--striped.current-row td{background-color:#e9e9ea}.custom-theme .el-table__body tr.hover-row.current-row>td,.custom-theme .el-table__body tr.hover-row.el-table__row--striped.current-row>td,.custom-theme .el-table__body tr.hover-row.el-table__row--striped>td,.custom-theme .el-table__body tr.hover-row>td{background-color:#e9e9ea}.custom-theme .el-table__body tr.current-row>td{background-color:#e9e9ea}.custom-theme .el-table__column-resize-proxy{position:absolute;left:200px;top:0;bottom:0;width:0;border-left:1px solid #e6ebf5;z-index:10}.custom-theme .el-table__column-filter-trigger{display:inline-block;line-height:34px;cursor:pointer}.custom-theme .el-table__column-filter-trigger i{color:#0a76a4;font-size:12px;-webkit-transform:scale(.75);transform:scale(.75)}.custom-theme .el-table--enable-row-transition .el-table__body td{-webkit-transition:background-color .25s ease;transition:background-color .25s ease}.custom-theme .el-table--enable-row-hover .el-table__body tr:hover>td{background-color:#f5f7fa}.custom-theme .el-table--fluid-height .el-table__fixed,.custom-theme .el-table--fluid-height .el-table__fixed-right{bottom:0;overflow:hidden}.custom-theme .el-checkbox{color:#5a5e66;font-weight:500;font-size:14px;position:relative;cursor:pointer;display:inline-block;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-theme .el-checkbox.is-bordered{padding:9px 20px 9px 10px;border-radius:4px;border:1px solid #d8dce5}.custom-theme .el-checkbox.is-bordered.is-checked{border-color:#262729}.custom-theme .el-checkbox.is-bordered.is-disabled{border-color:#e6ebf5;cursor:not-allowed}.custom-theme .el-checkbox.is-bordered+.el-checkbox.is-bordered{margin-left:10px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium{padding:7px 20px 7px 10px;border-radius:4px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__label{line-height:17px;font-size:14px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__inner{height:14px;width:14px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small{padding:3px 15px 7px 10px;border-radius:3px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{line-height:15px;font-size:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner{height:12px;width:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner::after{height:6px;width:2px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini{padding:1px 15px 5px 10px;border-radius:3px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__label{line-height:12px;font-size:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner{height:12px;width:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner::after{height:6px;width:2px}.custom-theme .el-checkbox__input{white-space:nowrap;cursor:pointer;outline:0;display:inline-block;line-height:1;position:relative;vertical-align:middle}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5;cursor:not-allowed}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner::after{cursor:not-allowed;border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner+.el-checkbox__label{cursor:not-allowed}.custom-theme .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5}.custom-theme .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner::after{border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5}.custom-theme .el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner::before{background-color:#b4bccc;border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled+span.el-checkbox__label{color:#b4bccc;cursor:not-allowed}.custom-theme .el-checkbox__input.is-checked .el-checkbox__inner{background-color:#262729;border-color:#262729}.custom-theme .el-checkbox__input.is-checked .el-checkbox__inner::after{-webkit-transform:rotate(45deg) scaleY(1);transform:rotate(45deg) scaleY(1)}.custom-theme .el-checkbox__input.is-checked+.el-checkbox__label{color:#262729}.custom-theme .el-checkbox__input.is-focus .el-checkbox__inner{border-color:#262729}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#262729;border-color:#262729}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner::before{content:'';position:absolute;display:block;background-color:#fff;height:2px;-webkit-transform:scale(.5);transform:scale(.5);left:0;right:0;top:5px}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner::after{display:none}.custom-theme .el-checkbox__inner{display:inline-block;position:relative;border:1px solid #d8dce5;border-radius:2px;-webkit-box-sizing:border-box;box-sizing:border-box;width:14px;height:14px;background-color:#fff;z-index:1;-webkit-transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46);transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46)}.custom-theme .el-checkbox__inner:hover{border-color:#262729}.custom-theme .el-checkbox__inner::after{-webkit-box-sizing:content-box;box-sizing:content-box;content:\"\";border:1px solid #fff;border-left:0;border-top:0;height:7px;left:4px;position:absolute;top:1px;-webkit-transform:rotate(45deg) scaleY(0);transform:rotate(45deg) scaleY(0);width:3px;-webkit-transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms,-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;-webkit-transform-origin:center;transform-origin:center}.custom-theme .el-checkbox__original{opacity:0;outline:0;position:absolute;margin:0;width:0;height:0;left:-999px}.custom-theme .el-checkbox__label{display:inline-block;padding-left:10px;line-height:19px;font-size:14px}.custom-theme .el-checkbox+.el-checkbox{margin-left:30px}.custom-theme .el-checkbox-button{position:relative;display:inline-block}.custom-theme .el-checkbox-button__inner{display:inline-block;line-height:1;font-weight:500;white-space:nowrap;vertical-align:middle;cursor:pointer;background:#fff;border:1px solid #d8dce5;border-left:0;color:#5a5e66;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;position:relative;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:14px;border-radius:0}.custom-theme .el-checkbox-button__inner.is-round{padding:12px 20px}.custom-theme .el-checkbox-button__inner:hover{color:#262729}.custom-theme .el-checkbox-button__inner [class*=el-icon-]{line-height:.9}.custom-theme .el-checkbox-button__inner [class*=el-icon-]+span{margin-left:5px}.custom-theme .el-checkbox-button__original{opacity:0;outline:0;position:absolute;margin:0;left:-999px}.custom-theme .el-checkbox-button.is-checked .el-checkbox-button__inner{color:#fff;background-color:#262729;border-color:#262729;-webkit-box-shadow:-1px 0 0 0 #7d7d7f;box-shadow:-1px 0 0 0 #7d7d7f}.custom-theme .el-checkbox-button.is-disabled .el-checkbox-button__inner{color:#b4bccc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#e6ebf5;-webkit-box-shadow:none;box-shadow:none}.custom-theme .el-checkbox-button:first-child .el-checkbox-button__inner{border-left:1px solid #d8dce5;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.custom-theme .el-checkbox-button.is-focus .el-checkbox-button__inner{border-color:#262729}.custom-theme .el-checkbox-button:last-child .el-checkbox-button__inner{border-radius:0 4px 4px 0}.custom-theme .el-checkbox-button--medium .el-checkbox-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.custom-theme .el-checkbox-button--medium .el-checkbox-button__inner.is-round{padding:10px 20px}.custom-theme .el-checkbox-button--small .el-checkbox-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.custom-theme .el-checkbox-button--small .el-checkbox-button__inner.is-round{padding:9px 15px}.custom-theme .el-checkbox-button--mini .el-checkbox-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.custom-theme .el-checkbox-button--mini .el-checkbox-button__inner.is-round{padding:7px 15px}.custom-theme .el-checkbox-group{font-size:0}.custom-theme .el-tag{background-color:rgba(38,39,41,.1);display:inline-block;padding:0 10px;height:32px;line-height:30px;font-size:12px;color:#262729;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid rgba(38,39,41,.2);white-space:nowrap}.custom-theme .el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:18px;width:18px;line-height:18px;vertical-align:middle;top:-1px;right:-5px;color:#262729}.custom-theme .el-tag .el-icon-close::before{display:block}.custom-theme .el-tag .el-icon-close:hover{background-color:#262729;color:#fff}.custom-theme .el-tag--info{background-color:rgba(10,118,164,.1);border-color:rgba(10,118,164,.2);color:#0a76a4}.custom-theme .el-tag--info.is-hit{border-color:#0a76a4}.custom-theme .el-tag--info .el-tag__close{color:#0a76a4}.custom-theme .el-tag--info .el-tag__close:hover{background-color:#0a76a4;color:#fff}.custom-theme .el-tag--success{background-color:rgba(64,145,103,.1);border-color:rgba(64,145,103,.2);color:#409167}.custom-theme .el-tag--success.is-hit{border-color:#409167}.custom-theme .el-tag--success .el-tag__close{color:#409167}.custom-theme .el-tag--success .el-tag__close:hover{background-color:#409167;color:#fff}.custom-theme .el-tag--warning{background-color:rgba(157,164,8,.1);border-color:rgba(157,164,8,.2);color:#9da408}.custom-theme .el-tag--warning.is-hit{border-color:#9da408}.custom-theme .el-tag--warning .el-tag__close{color:#9da408}.custom-theme .el-tag--warning .el-tag__close:hover{background-color:#9da408;color:#fff}.custom-theme .el-tag--danger{background-color:rgba(179,69,14,.1);border-color:rgba(179,69,14,.2);color:#b3450e}.custom-theme .el-tag--danger.is-hit{border-color:#b3450e}.custom-theme .el-tag--danger .el-tag__close{color:#b3450e}.custom-theme .el-tag--danger .el-tag__close:hover{background-color:#b3450e;color:#fff}.custom-theme .el-tag--medium{height:28px;line-height:26px}.custom-theme .el-tag--medium .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-tag--small{height:24px;padding:0 8px;line-height:22px}.custom-theme .el-tag--small .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-tag--mini{height:20px;padding:0 5px;line-height:19px}.custom-theme .el-tag--mini .el-icon-close{margin-left:-3px;-webkit-transform:scale(.7);transform:scale(.7)}.custom-theme .el-table-column--selection .cell{padding-left:14px;padding-right:14px}.custom-theme .el-table-filter{border:solid 1px #e6ebf5;border-radius:2px;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:2px 0}.custom-theme .el-table-filter__list{padding:5px 0;margin:0;list-style:none;min-width:100px}.custom-theme .el-table-filter__list-item{line-height:36px;padding:0 10px;cursor:pointer;font-size:14px}.custom-theme .el-table-filter__list-item:hover{background-color:#e9e9ea;color:#515254}.custom-theme .el-table-filter__list-item.is-active{background-color:#262729;color:#fff}.custom-theme .el-table-filter__content{min-width:100px}.custom-theme .el-table-filter__bottom{border-top:1px solid #e6ebf5;padding:8px}.custom-theme .el-table-filter__bottom button{background:0 0;border:none;color:#5a5e66;cursor:pointer;font-size:13px;padding:0 3px}.custom-theme .el-table-filter__bottom button:hover{color:#262729}.custom-theme .el-table-filter__bottom button:focus{outline:0}.custom-theme .el-table-filter__bottom button.is-disabled{color:#b4bccc;cursor:not-allowed}.custom-theme .el-table-filter__checkbox-group{padding:10px}.custom-theme .el-table-filter__checkbox-group label.el-checkbox{display:block;margin-bottom:8px;margin-left:5px}.custom-theme .el-table-filter__checkbox-group .el-checkbox:last-child{margin-bottom:0}.custom-theme .el-date-table{font-size:12px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-theme .el-date-table.is-week-mode .el-date-table__row:hover div{background-color:#edf2fc}.custom-theme .el-date-table.is-week-mode .el-date-table__row:hover td.available:hover{color:#5a5e66}.custom-theme .el-date-table.is-week-mode .el-date-table__row:hover td:first-child div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.custom-theme .el-date-table.is-week-mode .el-date-table__row:hover td:last-child div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.custom-theme .el-date-table.is-week-mode .el-date-table__row.current div{background-color:#edf2fc}.custom-theme .el-date-table td{width:32px;height:30px;padding:4px 0;-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center;cursor:pointer;position:relative}.custom-theme .el-date-table td div{height:30px;padding:3px 0;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-date-table td span{width:24px;height:24px;display:block;margin:0 auto;line-height:24px;position:absolute;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);border-radius:50%}.custom-theme .el-date-table td.next-month,.custom-theme .el-date-table td.prev-month{color:#b4bccc}.custom-theme .el-date-table td.today{position:relative}.custom-theme .el-date-table td.today span{color:#262729}.custom-theme .el-date-table td.today.end-date span,.custom-theme .el-date-table td.today.start-date span{color:#fff}.custom-theme .el-date-table td.available:hover{color:#262729}.custom-theme .el-date-table td.in-range div{background-color:#edf2fc}.custom-theme .el-date-table td.in-range div:hover{background-color:#edf2fc}.custom-theme .el-date-table td.current:not(.disabled) span{color:#fff;background-color:#262729}.custom-theme .el-date-table td.end-date div,.custom-theme .el-date-table td.start-date div{color:#fff}.custom-theme .el-date-table td.end-date span,.custom-theme .el-date-table td.start-date span{background-color:#262729}.custom-theme .el-date-table td.start-date div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.custom-theme .el-date-table td.end-date div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.custom-theme .el-date-table td.disabled div{background-color:#f5f7fa;opacity:1;cursor:not-allowed;color:#b4bccc}.custom-theme .el-date-table td.week{font-size:80%;color:#5a5e66}.custom-theme .el-date-table th{padding:5px;color:#5a5e66;font-weight:400;border-bottom:solid 1px #e6ebf5}.custom-theme .el-month-table{font-size:12px;margin:-1px;border-collapse:collapse}.custom-theme .el-month-table td{text-align:center;padding:20px 3px;cursor:pointer}.custom-theme .el-month-table td.disabled .cell{background-color:#f5f7fa;cursor:not-allowed;color:#b4bccc}.custom-theme .el-month-table td.disabled .cell:hover{color:#b4bccc}.custom-theme .el-month-table td .cell{width:48px;height:32px;display:block;line-height:32px;color:#5a5e66;margin:0 auto}.custom-theme .el-month-table td .cell:hover{color:#262729}.custom-theme .el-month-table td.current:not(.disabled) .cell{color:#262729}.custom-theme .el-year-table{font-size:12px;margin:-1px;border-collapse:collapse}.custom-theme .el-year-table .el-icon{color:#2d2f33}.custom-theme .el-year-table td{text-align:center;padding:20px 3px;cursor:pointer}.custom-theme .el-year-table td.disabled .cell{background-color:#f5f7fa;cursor:not-allowed;color:#b4bccc}.custom-theme .el-year-table td.disabled .cell:hover{color:#b4bccc}.custom-theme .el-year-table td .cell{width:48px;height:32px;display:block;line-height:32px;color:#5a5e66;margin:0 auto}.custom-theme .el-year-table td .cell:hover{color:#262729}.custom-theme .el-year-table td.current:not(.disabled) .cell{color:#262729}.custom-theme .el-time-spinner.has-seconds .el-time-spinner__wrapper{width:33.3%}.custom-theme .el-time-spinner.has-seconds .el-time-spinner__wrapper:nth-child(2){margin-left:1%}.custom-theme .el-time-spinner__wrapper{max-height:190px;overflow:auto;display:inline-block;width:50%;vertical-align:top;position:relative}.custom-theme .el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default){padding-bottom:15px}.custom-theme .el-time-spinner__wrapper.is-arrow{-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center;overflow:hidden}.custom-theme .el-time-spinner__wrapper.is-arrow .el-time-spinner__list{-webkit-transform:translateY(-32px);transform:translateY(-32px)}.custom-theme .el-time-spinner__wrapper.is-arrow .el-time-spinner__item:hover:not(.disabled):not(.active){background:#fff;cursor:default}.custom-theme .el-time-spinner__arrow{font-size:12px;color:#878d99;position:absolute;left:0;width:100%;z-index:1;text-align:center;height:30px;line-height:30px;cursor:pointer}.custom-theme .el-time-spinner__arrow:hover{color:#262729}.custom-theme .el-time-spinner__arrow.el-icon-arrow-up{top:10px}.custom-theme .el-time-spinner__arrow.el-icon-arrow-down{bottom:10px}.custom-theme .el-time-spinner__input.el-input{width:70%}.custom-theme .el-time-spinner__input.el-input .el-input__inner{padding:0;text-align:center}.custom-theme .el-time-spinner__list{padding:0;margin:0;list-style:none;text-align:center}.custom-theme .el-time-spinner__list::after,.custom-theme .el-time-spinner__list::before{content:'';display:block;width:100%;height:80px}.custom-theme .el-time-spinner__item{height:32px;line-height:32px;font-size:12px;color:#5a5e66}.custom-theme .el-time-spinner__item:hover:not(.disabled):not(.active){background:#f5f7fa;cursor:pointer}.custom-theme .el-time-spinner__item.active:not(.disabled){color:#2d2f33;font-weight:700}.custom-theme .el-time-spinner__item.disabled{color:#b4bccc;cursor:not-allowed}.custom-theme .fade-in-linear-enter-active,.custom-theme .fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.custom-theme .fade-in-linear-enter,.custom-theme .fade-in-linear-leave,.custom-theme .fade-in-linear-leave-active{opacity:0}.custom-theme .el-fade-in-linear-enter-active,.custom-theme .el-fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.custom-theme .el-fade-in-linear-enter,.custom-theme .el-fade-in-linear-leave,.custom-theme .el-fade-in-linear-leave-active{opacity:0}.custom-theme .el-fade-in-enter-active,.custom-theme .el-fade-in-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.custom-theme .el-fade-in-enter,.custom-theme .el-fade-in-leave-active{opacity:0}.custom-theme .el-zoom-in-center-enter-active,.custom-theme .el-zoom-in-center-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.custom-theme .el-zoom-in-center-enter,.custom-theme .el-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.custom-theme .el-zoom-in-top-enter-active,.custom-theme .el-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:center top;transform-origin:center top}.custom-theme .el-zoom-in-top-enter,.custom-theme .el-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.custom-theme .el-zoom-in-bottom-enter-active,.custom-theme .el-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:center bottom;transform-origin:center bottom}.custom-theme .el-zoom-in-bottom-enter,.custom-theme .el-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.custom-theme .el-zoom-in-left-enter-active,.custom-theme .el-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1,1);transform:scale(1,1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:top left;transform-origin:top left}.custom-theme .el-zoom-in-left-enter,.custom-theme .el-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45,.45);transform:scale(.45,.45)}.custom-theme .collapse-transition{-webkit-transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out;transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out}.custom-theme .horizontal-collapse-transition{-webkit-transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out;transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out}.custom-theme .el-list-enter-active,.custom-theme .el-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.custom-theme .el-list-enter,.custom-theme .el-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.custom-theme .el-opacity-transition{-webkit-transition:opacity .3s cubic-bezier(.55,0,.1,1);transition:opacity .3s cubic-bezier(.55,0,.1,1)}.custom-theme .el-date-editor{position:relative;display:inline-block;text-align:left}.custom-theme .el-date-editor.el-input,.custom-theme .el-date-editor.el-input__inner{width:220px}.custom-theme .el-date-editor--daterange.el-input,.custom-theme .el-date-editor--daterange.el-input__inner,.custom-theme .el-date-editor--timerange.el-input,.custom-theme .el-date-editor--timerange.el-input__inner{width:350px}.custom-theme .el-date-editor--datetimerange.el-input,.custom-theme .el-date-editor--datetimerange.el-input__inner{width:400px}.custom-theme .el-date-editor .el-range__icon{font-size:14px;margin-left:-5px;color:#b4bccc;float:left;line-height:32px}.custom-theme .el-date-editor .el-range-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:none;outline:0;display:inline-block;height:100%;margin:0;padding:0;width:39%;text-align:center;font-size:14px;color:#5a5e66}.custom-theme .el-date-editor .el-range-input::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-input:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-input::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-input::placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-separator{display:inline-block;height:100%;padding:0 5px;margin:0;text-align:center;line-height:32px;font-size:14px;width:5%;color:#2d2f33}.custom-theme .el-date-editor .el-range__close-icon{font-size:14px;color:#b4bccc;width:25px;display:inline-block;float:right;line-height:32px}.custom-theme .el-range-editor.el-input__inner{padding:3px 10px}.custom-theme .el-range-editor.is-active{border-color:#262729}.custom-theme .el-range-editor.is-active:hover{border-color:#262729}.custom-theme .el-range-editor--medium.el-input__inner{height:36px}.custom-theme .el-range-editor--medium .el-range-separator{line-height:28px;font-size:14px}.custom-theme .el-range-editor--medium .el-range-input{font-size:14px}.custom-theme .el-range-editor--medium .el-range__close-icon,.custom-theme .el-range-editor--medium .el-range__icon{line-height:28px}.custom-theme .el-range-editor--small.el-input__inner{height:32px}.custom-theme .el-range-editor--small .el-range-separator{line-height:24px;font-size:13px}.custom-theme .el-range-editor--small .el-range-input{font-size:13px}.custom-theme .el-range-editor--small .el-range__close-icon,.custom-theme .el-range-editor--small .el-range__icon{line-height:24px}.custom-theme .el-range-editor--mini.el-input__inner{height:28px}.custom-theme .el-range-editor--mini .el-range-separator{line-height:20px;font-size:12px}.custom-theme .el-range-editor--mini .el-range-input{font-size:12px}.custom-theme .el-range-editor--mini .el-range__close-icon,.custom-theme .el-range-editor--mini .el-range__icon{line-height:20px}.custom-theme .el-range-editor.is-disabled{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-range-editor.is-disabled:focus,.custom-theme .el-range-editor.is-disabled:hover{border-color:#dfe4ed}.custom-theme .el-range-editor.is-disabled input{background-color:#f5f7fa;color:#b4bccc;cursor:not-allowed}.custom-theme .el-range-editor.is-disabled input::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled input:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled input::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled input::placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled .el-range-separator{color:#b4bccc}.custom-theme .el-picker-panel{color:#5a5e66;border:1px solid #dfe4ed;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);background:#fff;border-radius:4px;line-height:30px;margin:5px 0}.custom-theme .el-picker-panel__body-wrapper::after,.custom-theme .el-picker-panel__body::after{content:\"\";display:table;clear:both}.custom-theme .el-picker-panel__content{position:relative;margin:15px}.custom-theme .el-picker-panel__footer{border-top:1px solid #e4e4e4;padding:4px;text-align:right;background-color:#fff;position:relative;font-size:0}.custom-theme .el-picker-panel__shortcut{display:block;width:100%;border:0;background-color:transparent;line-height:28px;font-size:14px;color:#5a5e66;padding-left:12px;text-align:left;outline:0;cursor:pointer}.custom-theme .el-picker-panel__shortcut:hover{color:#262729}.custom-theme .el-picker-panel__shortcut.active{background-color:#e6f1fe;color:#262729}.custom-theme .el-picker-panel__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.custom-theme .el-picker-panel__btn[disabled]{color:#ccc;cursor:not-allowed}.custom-theme .el-picker-panel__icon-btn{font-size:12px;color:#2d2f33;border:0;background:0 0;cursor:pointer;outline:0;margin-top:8px}.custom-theme .el-picker-panel__icon-btn:hover{color:#262729}.custom-theme .el-picker-panel__icon-btn.is-disabled{color:#bbb}.custom-theme .el-picker-panel__icon-btn.is-disabled:hover{cursor:not-allowed}.custom-theme .el-picker-panel__link-btn{vertical-align:middle}.custom-theme .el-picker-panel .popper__arrow{-webkit-transform:translateX(-400%);transform:translateX(-400%)}.custom-theme .el-picker-panel [slot=sidebar],.custom-theme .el-picker-panel__sidebar{position:absolute;top:0;bottom:0;width:110px;border-right:1px solid #e4e4e4;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;background-color:#fff;overflow:auto}.custom-theme .el-picker-panel [slot=sidebar]+.el-picker-panel__body,.custom-theme .el-picker-panel__sidebar+.el-picker-panel__body{margin-left:110px}.custom-theme .el-date-picker{width:322px}.custom-theme .el-date-picker.has-sidebar.has-time{width:434px}.custom-theme .el-date-picker.has-sidebar{width:438px}.custom-theme .el-date-picker.has-time .el-picker-panel__body-wrapper{position:relative}.custom-theme .el-date-picker .el-picker-panel__content{width:292px}.custom-theme .el-date-picker table{table-layout:fixed;width:100%}.custom-theme .el-date-picker__editor-wrap{position:relative;display:table-cell;padding:0 5px}.custom-theme .el-date-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px 5px;display:table;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-date-picker__header{margin:12px;text-align:center}.custom-theme .el-date-picker__header--bordered{margin-bottom:0;padding-bottom:12px;border-bottom:solid 1px #e6ebf5}.custom-theme .el-date-picker__header--bordered+.el-picker-panel__content{margin-top:0}.custom-theme .el-date-picker__header-label{font-size:16px;font-weight:500;padding:0 5px;line-height:22px;text-align:center;cursor:pointer;color:#5a5e66}.custom-theme .el-date-picker__header-label:hover{color:#262729}.custom-theme .el-date-picker__header-label.active{color:#262729}.custom-theme .el-date-picker__prev-btn{float:left}.custom-theme .el-date-picker__next-btn{float:right}.custom-theme .el-date-picker__time-wrap{padding:10px;text-align:center}.custom-theme .el-date-picker__time-label{float:left;cursor:pointer;line-height:30px;margin-left:10px}.custom-theme .el-date-range-picker{width:646px}.custom-theme .el-date-range-picker.has-sidebar{width:756px}.custom-theme .el-date-range-picker table{table-layout:fixed;width:100%}.custom-theme .el-date-range-picker .el-picker-panel__body{min-width:513px}.custom-theme .el-date-range-picker .el-picker-panel__content{margin:0}.custom-theme .el-date-range-picker__header{position:relative;text-align:center;height:28px}.custom-theme .el-date-range-picker__header [class*=arrow-left]{float:left}.custom-theme .el-date-range-picker__header [class*=arrow-right]{float:right}.custom-theme .el-date-range-picker__header div{font-size:16px;font-weight:500;margin-right:50px}.custom-theme .el-date-range-picker__content{float:left;width:50%;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:16px}.custom-theme .el-date-range-picker__content.is-left{border-right:1px solid #e4e4e4}.custom-theme .el-date-range-picker__content.is-right .el-date-range-picker__header div{margin-left:50px;margin-right:50px}.custom-theme .el-date-range-picker__editors-wrap{-webkit-box-sizing:border-box;box-sizing:border-box;display:table-cell}.custom-theme .el-date-range-picker__editors-wrap.is-right{text-align:right}.custom-theme .el-date-range-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px 5px;display:table;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-date-range-picker__time-header>.el-icon-arrow-right{font-size:20px;vertical-align:middle;display:table-cell;color:#2d2f33}.custom-theme .el-date-range-picker__time-picker-wrap{position:relative;display:table-cell;padding:0 5px}.custom-theme .el-date-range-picker__time-picker-wrap .el-picker-panel{position:absolute;top:13px;right:0;z-index:1;background:#fff}.custom-theme .el-time-range-picker{width:354px;overflow:visible}.custom-theme .el-time-range-picker__content{position:relative;text-align:center;padding:10px}.custom-theme .el-time-range-picker__cell{-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:4px 7px 7px;width:50%;display:inline-block}.custom-theme .el-time-range-picker__header{margin-bottom:5px;text-align:center;font-size:14px}.custom-theme .el-time-range-picker__body{border-radius:2px;border:1px solid #dfe4ed}.custom-theme .el-time-panel{margin:5px 0;border:solid 1px #dfe4ed;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:2px;position:absolute;width:180px;left:0;z-index:1000;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-theme .el-time-panel__content{font-size:0;position:relative;overflow:hidden}.custom-theme .el-time-panel__content::after,.custom-theme .el-time-panel__content::before{content:\"\";top:50%;position:absolute;margin-top:-15px;height:32px;z-index:-1;left:0;right:0;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;text-align:left;border-top:1px solid #dfe4ed;border-bottom:1px solid #dfe4ed}.custom-theme .el-time-panel__content::after{left:50%;margin-left:12%;margin-right:12%}.custom-theme .el-time-panel__content::before{padding-left:50%;margin-right:12%;margin-left:12%}.custom-theme .el-time-panel__content.has-seconds::after{left:calc(100% / 3 * 2)}.custom-theme .el-time-panel__content.has-seconds::before{padding-left:calc(100% / 3)}.custom-theme .el-time-panel__footer{border-top:1px solid #e4e4e4;padding:4px;height:36px;line-height:25px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-time-panel__btn{border:none;line-height:28px;padding:0 5px;margin:0 5px;cursor:pointer;background-color:transparent;outline:0;font-size:12px;color:#2d2f33}.custom-theme .el-time-panel__btn.confirm{font-weight:800;color:#262729}.custom-theme .el-time-panel .popper__arrow{-webkit-transform:translateX(-400%);transform:translateX(-400%)}.custom-theme .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.custom-theme .el-input::-webkit-scrollbar{z-index:11;width:6px}.custom-theme .el-input::-webkit-scrollbar:horizontal{height:6px}.custom-theme .el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.custom-theme .el-input::-webkit-scrollbar-corner{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.custom-theme .el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.custom-theme .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input__inner:hover{border-color:#b4bccc}.custom-theme .el-input__inner:focus{outline:0;border-color:#262729}.custom-theme .el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.custom-theme .el-input__suffix-inner{pointer-events:all}.custom-theme .el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s}.custom-theme .el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.custom-theme .el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.custom-theme .el-input__validateIcon{pointer-events:none}.custom-theme .el-input.is-active .el-input__inner{outline:0;border-color:#262729}.custom-theme .el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__icon{cursor:not-allowed}.custom-theme .el-input--suffix .el-input__inner{padding-right:30px}.custom-theme .el-input--prefix .el-input__inner{padding-left:30px}.custom-theme .el-input--medium{font-size:14px}.custom-theme .el-input--medium .el-input__inner{height:36px}.custom-theme .el-input--medium .el-input__icon{line-height:36px}.custom-theme .el-input--small{font-size:13px}.custom-theme .el-input--small .el-input__inner{height:32px}.custom-theme .el-input--small .el-input__icon{line-height:32px}.custom-theme .el-input--mini{font-size:12px}.custom-theme .el-input--mini .el-input__inner{height:28px}.custom-theme .el-input--mini .el-input__icon{line-height:28px}.custom-theme .el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.custom-theme .el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.custom-theme .el-input-group__append,.custom-theme .el-input-group__prepend{background-color:#f5f7fa;color:#0a76a4;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.custom-theme .el-input-group__append:focus,.custom-theme .el-input-group__prepend:focus{outline:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-select,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-select{display:inline-block;margin:-20px}.custom-theme .el-input-group__append button.el-button,.custom-theme .el-input-group__append div.el-select .el-input__inner,.custom-theme .el-input-group__append div.el-select:hover .el-input__inner,.custom-theme .el-input-group__prepend button.el-button,.custom-theme .el-input-group__prepend div.el-select .el-input__inner,.custom-theme .el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-input,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-input{font-size:inherit}.custom-theme .el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-textarea{display:inline-block;width:100%;vertical-align:bottom}.custom-theme .el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:hover{border-color:#b4bccc}.custom-theme .el-textarea__inner:focus{outline:0;border-color:#262729}.custom-theme .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-scrollbar{overflow:hidden;position:relative}.custom-theme .el-scrollbar:active>.el-scrollbar__bar,.custom-theme .el-scrollbar:focus>.el-scrollbar__bar,.custom-theme .el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity 340ms ease-out;transition:opacity 340ms ease-out}.custom-theme .el-scrollbar__wrap{overflow:scroll;height:100%}.custom-theme .el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.custom-theme .el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(135,141,153,.3);-webkit-transition:.3s background-color;transition:.3s background-color}.custom-theme .el-scrollbar__thumb:hover{background-color:rgba(135,141,153,.5)}.custom-theme .el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity 120ms ease-out;transition:opacity 120ms ease-out}.custom-theme .el-scrollbar__bar.is-vertical{width:6px;top:2px}.custom-theme .el-scrollbar__bar.is-vertical>div{width:100%}.custom-theme .el-scrollbar__bar.is-horizontal{height:6px;left:2px}.custom-theme .el-scrollbar__bar.is-horizontal>div{height:100%}.custom-theme .el-popper .popper__arrow,.custom-theme .el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.custom-theme .el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.custom-theme .el-popper .popper__arrow::after{content:\" \";border-width:6px}.custom-theme .el-popper[x-placement^=top]{margin-bottom:12px}.custom-theme .el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.custom-theme .el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.custom-theme .el-popper[x-placement^=bottom]{margin-top:12px}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.custom-theme .el-popper[x-placement^=right]{margin-left:12px}.custom-theme .el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.custom-theme .el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.custom-theme .el-popper[x-placement^=left]{margin-right:12px}.custom-theme .el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.custom-theme .el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.custom-theme .fade-in-linear-enter-active,.custom-theme .fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.custom-theme .fade-in-linear-enter,.custom-theme .fade-in-linear-leave,.custom-theme .fade-in-linear-leave-active{opacity:0}.custom-theme .el-fade-in-linear-enter-active,.custom-theme .el-fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.custom-theme .el-fade-in-linear-enter,.custom-theme .el-fade-in-linear-leave,.custom-theme .el-fade-in-linear-leave-active{opacity:0}.custom-theme .el-fade-in-enter-active,.custom-theme .el-fade-in-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.custom-theme .el-fade-in-enter,.custom-theme .el-fade-in-leave-active{opacity:0}.custom-theme .el-zoom-in-center-enter-active,.custom-theme .el-zoom-in-center-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.custom-theme .el-zoom-in-center-enter,.custom-theme .el-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.custom-theme .el-zoom-in-top-enter-active,.custom-theme .el-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:center top;transform-origin:center top}.custom-theme .el-zoom-in-top-enter,.custom-theme .el-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.custom-theme .el-zoom-in-bottom-enter-active,.custom-theme .el-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:center bottom;transform-origin:center bottom}.custom-theme .el-zoom-in-bottom-enter,.custom-theme .el-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.custom-theme .el-zoom-in-left-enter-active,.custom-theme .el-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1,1);transform:scale(1,1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:top left;transform-origin:top left}.custom-theme .el-zoom-in-left-enter,.custom-theme .el-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45,.45);transform:scale(.45,.45)}.custom-theme .collapse-transition{-webkit-transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out;transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out}.custom-theme .horizontal-collapse-transition{-webkit-transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out;transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out}.custom-theme .el-list-enter-active,.custom-theme .el-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.custom-theme .el-list-enter,.custom-theme .el-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.custom-theme .el-opacity-transition{-webkit-transition:opacity .3s cubic-bezier(.55,0,.1,1);transition:opacity .3s cubic-bezier(.55,0,.1,1)}.custom-theme .el-date-editor{position:relative;display:inline-block;text-align:left}.custom-theme .el-date-editor.el-input,.custom-theme .el-date-editor.el-input__inner{width:220px}.custom-theme .el-date-editor--daterange.el-input,.custom-theme .el-date-editor--daterange.el-input__inner,.custom-theme .el-date-editor--timerange.el-input,.custom-theme .el-date-editor--timerange.el-input__inner{width:350px}.custom-theme .el-date-editor--datetimerange.el-input,.custom-theme .el-date-editor--datetimerange.el-input__inner{width:400px}.custom-theme .el-date-editor .el-range__icon{font-size:14px;margin-left:-5px;color:#b4bccc;float:left;line-height:32px}.custom-theme .el-date-editor .el-range-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:none;outline:0;display:inline-block;height:100%;margin:0;padding:0;width:39%;text-align:center;font-size:14px;color:#5a5e66}.custom-theme .el-date-editor .el-range-input::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-input:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-input::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-input::placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-separator{display:inline-block;height:100%;padding:0 5px;margin:0;text-align:center;line-height:32px;font-size:14px;width:5%;color:#2d2f33}.custom-theme .el-date-editor .el-range__close-icon{font-size:14px;color:#b4bccc;width:25px;display:inline-block;float:right;line-height:32px}.custom-theme .el-range-editor.el-input__inner{padding:3px 10px}.custom-theme .el-range-editor.is-active{border-color:#262729}.custom-theme .el-range-editor.is-active:hover{border-color:#262729}.custom-theme .el-range-editor--medium.el-input__inner{height:36px}.custom-theme .el-range-editor--medium .el-range-separator{line-height:28px;font-size:14px}.custom-theme .el-range-editor--medium .el-range-input{font-size:14px}.custom-theme .el-range-editor--medium .el-range__close-icon,.custom-theme .el-range-editor--medium .el-range__icon{line-height:28px}.custom-theme .el-range-editor--small.el-input__inner{height:32px}.custom-theme .el-range-editor--small .el-range-separator{line-height:24px;font-size:13px}.custom-theme .el-range-editor--small .el-range-input{font-size:13px}.custom-theme .el-range-editor--small .el-range__close-icon,.custom-theme .el-range-editor--small .el-range__icon{line-height:24px}.custom-theme .el-range-editor--mini.el-input__inner{height:28px}.custom-theme .el-range-editor--mini .el-range-separator{line-height:20px;font-size:12px}.custom-theme .el-range-editor--mini .el-range-input{font-size:12px}.custom-theme .el-range-editor--mini .el-range__close-icon,.custom-theme .el-range-editor--mini .el-range__icon{line-height:20px}.custom-theme .el-range-editor.is-disabled{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-range-editor.is-disabled:focus,.custom-theme .el-range-editor.is-disabled:hover{border-color:#dfe4ed}.custom-theme .el-range-editor.is-disabled input{background-color:#f5f7fa;color:#b4bccc;cursor:not-allowed}.custom-theme .el-range-editor.is-disabled input::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled input:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled input::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled input::placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled .el-range-separator{color:#b4bccc}.custom-theme .el-picker-panel{color:#5a5e66;border:1px solid #dfe4ed;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);background:#fff;border-radius:4px;line-height:30px;margin:5px 0}.custom-theme .el-picker-panel__body-wrapper::after,.custom-theme .el-picker-panel__body::after{content:\"\";display:table;clear:both}.custom-theme .el-picker-panel__content{position:relative;margin:15px}.custom-theme .el-picker-panel__footer{border-top:1px solid #e4e4e4;padding:4px;text-align:right;background-color:#fff;position:relative;font-size:0}.custom-theme .el-picker-panel__shortcut{display:block;width:100%;border:0;background-color:transparent;line-height:28px;font-size:14px;color:#5a5e66;padding-left:12px;text-align:left;outline:0;cursor:pointer}.custom-theme .el-picker-panel__shortcut:hover{color:#262729}.custom-theme .el-picker-panel__shortcut.active{background-color:#e6f1fe;color:#262729}.custom-theme .el-picker-panel__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.custom-theme .el-picker-panel__btn[disabled]{color:#ccc;cursor:not-allowed}.custom-theme .el-picker-panel__icon-btn{font-size:12px;color:#2d2f33;border:0;background:0 0;cursor:pointer;outline:0;margin-top:8px}.custom-theme .el-picker-panel__icon-btn:hover{color:#262729}.custom-theme .el-picker-panel__icon-btn.is-disabled{color:#bbb}.custom-theme .el-picker-panel__icon-btn.is-disabled:hover{cursor:not-allowed}.custom-theme .el-picker-panel__link-btn{vertical-align:middle}.custom-theme .el-picker-panel .popper__arrow{-webkit-transform:translateX(-400%);transform:translateX(-400%)}.custom-theme .el-picker-panel [slot=sidebar],.custom-theme .el-picker-panel__sidebar{position:absolute;top:0;bottom:0;width:110px;border-right:1px solid #e4e4e4;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;background-color:#fff;overflow:auto}.custom-theme .el-picker-panel [slot=sidebar]+.el-picker-panel__body,.custom-theme .el-picker-panel__sidebar+.el-picker-panel__body{margin-left:110px}.custom-theme .el-date-picker{width:322px}.custom-theme .el-date-picker.has-sidebar.has-time{width:434px}.custom-theme .el-date-picker.has-sidebar{width:438px}.custom-theme .el-date-picker.has-time .el-picker-panel__body-wrapper{position:relative}.custom-theme .el-date-picker .el-picker-panel__content{width:292px}.custom-theme .el-date-picker table{table-layout:fixed;width:100%}.custom-theme .el-date-picker__editor-wrap{position:relative;display:table-cell;padding:0 5px}.custom-theme .el-date-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px 5px;display:table;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-date-picker__header{margin:12px;text-align:center}.custom-theme .el-date-picker__header--bordered{margin-bottom:0;padding-bottom:12px;border-bottom:solid 1px #e6ebf5}.custom-theme .el-date-picker__header--bordered+.el-picker-panel__content{margin-top:0}.custom-theme .el-date-picker__header-label{font-size:16px;font-weight:500;padding:0 5px;line-height:22px;text-align:center;cursor:pointer;color:#5a5e66}.custom-theme .el-date-picker__header-label:hover{color:#262729}.custom-theme .el-date-picker__header-label.active{color:#262729}.custom-theme .el-date-picker__prev-btn{float:left}.custom-theme .el-date-picker__next-btn{float:right}.custom-theme .el-date-picker__time-wrap{padding:10px;text-align:center}.custom-theme .el-date-picker__time-label{float:left;cursor:pointer;line-height:30px;margin-left:10px}.custom-theme .el-scrollbar{overflow:hidden;position:relative}.custom-theme .el-scrollbar:active>.el-scrollbar__bar,.custom-theme .el-scrollbar:focus>.el-scrollbar__bar,.custom-theme .el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity 340ms ease-out;transition:opacity 340ms ease-out}.custom-theme .el-scrollbar__wrap{overflow:scroll;height:100%}.custom-theme .el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.custom-theme .el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(135,141,153,.3);-webkit-transition:.3s background-color;transition:.3s background-color}.custom-theme .el-scrollbar__thumb:hover{background-color:rgba(135,141,153,.5)}.custom-theme .el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity 120ms ease-out;transition:opacity 120ms ease-out}.custom-theme .el-scrollbar__bar.is-vertical{width:6px;top:2px}.custom-theme .el-scrollbar__bar.is-vertical>div{width:100%}.custom-theme .el-scrollbar__bar.is-horizontal{height:6px;left:2px}.custom-theme .el-scrollbar__bar.is-horizontal>div{height:100%}.custom-theme .el-popper .popper__arrow,.custom-theme .el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.custom-theme .el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.custom-theme .el-popper .popper__arrow::after{content:\" \";border-width:6px}.custom-theme .el-popper[x-placement^=top]{margin-bottom:12px}.custom-theme .el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.custom-theme .el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.custom-theme .el-popper[x-placement^=bottom]{margin-top:12px}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.custom-theme .el-popper[x-placement^=right]{margin-left:12px}.custom-theme .el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.custom-theme .el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.custom-theme .el-popper[x-placement^=left]{margin-right:12px}.custom-theme .el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.custom-theme .el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.custom-theme .time-select{margin:5px 0;min-width:0}.custom-theme .time-select .el-picker-panel__content{max-height:200px;margin:0}.custom-theme .time-select-item{padding:8px 10px;font-size:14px;line-height:20px}.custom-theme .time-select-item.selected:not(.disabled){color:#262729;font-weight:700}.custom-theme .time-select-item.disabled{color:#dfe4ed;cursor:not-allowed}.custom-theme .time-select-item:hover{background-color:#f5f7fa;font-weight:700;cursor:pointer}.custom-theme .fade-in-linear-enter-active,.custom-theme .fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.custom-theme .fade-in-linear-enter,.custom-theme .fade-in-linear-leave,.custom-theme .fade-in-linear-leave-active{opacity:0}.custom-theme .el-fade-in-linear-enter-active,.custom-theme .el-fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.custom-theme .el-fade-in-linear-enter,.custom-theme .el-fade-in-linear-leave,.custom-theme .el-fade-in-linear-leave-active{opacity:0}.custom-theme .el-fade-in-enter-active,.custom-theme .el-fade-in-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.custom-theme .el-fade-in-enter,.custom-theme .el-fade-in-leave-active{opacity:0}.custom-theme .el-zoom-in-center-enter-active,.custom-theme .el-zoom-in-center-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.custom-theme .el-zoom-in-center-enter,.custom-theme .el-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.custom-theme .el-zoom-in-top-enter-active,.custom-theme .el-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:center top;transform-origin:center top}.custom-theme .el-zoom-in-top-enter,.custom-theme .el-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.custom-theme .el-zoom-in-bottom-enter-active,.custom-theme .el-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:center bottom;transform-origin:center bottom}.custom-theme .el-zoom-in-bottom-enter,.custom-theme .el-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.custom-theme .el-zoom-in-left-enter-active,.custom-theme .el-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1,1);transform:scale(1,1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:top left;transform-origin:top left}.custom-theme .el-zoom-in-left-enter,.custom-theme .el-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45,.45);transform:scale(.45,.45)}.custom-theme .collapse-transition{-webkit-transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out;transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out}.custom-theme .horizontal-collapse-transition{-webkit-transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out;transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out}.custom-theme .el-list-enter-active,.custom-theme .el-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.custom-theme .el-list-enter,.custom-theme .el-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.custom-theme .el-opacity-transition{-webkit-transition:opacity .3s cubic-bezier(.55,0,.1,1);transition:opacity .3s cubic-bezier(.55,0,.1,1)}.custom-theme .el-date-editor{position:relative;display:inline-block;text-align:left}.custom-theme .el-date-editor.el-input,.custom-theme .el-date-editor.el-input__inner{width:220px}.custom-theme .el-date-editor--daterange.el-input,.custom-theme .el-date-editor--daterange.el-input__inner,.custom-theme .el-date-editor--timerange.el-input,.custom-theme .el-date-editor--timerange.el-input__inner{width:350px}.custom-theme .el-date-editor--datetimerange.el-input,.custom-theme .el-date-editor--datetimerange.el-input__inner{width:400px}.custom-theme .el-date-editor .el-range__icon{font-size:14px;margin-left:-5px;color:#b4bccc;float:left;line-height:32px}.custom-theme .el-date-editor .el-range-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:none;outline:0;display:inline-block;height:100%;margin:0;padding:0;width:39%;text-align:center;font-size:14px;color:#5a5e66}.custom-theme .el-date-editor .el-range-input::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-input:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-input::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-input::placeholder{color:#b4bccc}.custom-theme .el-date-editor .el-range-separator{display:inline-block;height:100%;padding:0 5px;margin:0;text-align:center;line-height:32px;font-size:14px;width:5%;color:#2d2f33}.custom-theme .el-date-editor .el-range__close-icon{font-size:14px;color:#b4bccc;width:25px;display:inline-block;float:right;line-height:32px}.custom-theme .el-range-editor.el-input__inner{padding:3px 10px}.custom-theme .el-range-editor.is-active{border-color:#262729}.custom-theme .el-range-editor.is-active:hover{border-color:#262729}.custom-theme .el-range-editor--medium.el-input__inner{height:36px}.custom-theme .el-range-editor--medium .el-range-separator{line-height:28px;font-size:14px}.custom-theme .el-range-editor--medium .el-range-input{font-size:14px}.custom-theme .el-range-editor--medium .el-range__close-icon,.custom-theme .el-range-editor--medium .el-range__icon{line-height:28px}.custom-theme .el-range-editor--small.el-input__inner{height:32px}.custom-theme .el-range-editor--small .el-range-separator{line-height:24px;font-size:13px}.custom-theme .el-range-editor--small .el-range-input{font-size:13px}.custom-theme .el-range-editor--small .el-range__close-icon,.custom-theme .el-range-editor--small .el-range__icon{line-height:24px}.custom-theme .el-range-editor--mini.el-input__inner{height:28px}.custom-theme .el-range-editor--mini .el-range-separator{line-height:20px;font-size:12px}.custom-theme .el-range-editor--mini .el-range-input{font-size:12px}.custom-theme .el-range-editor--mini .el-range__close-icon,.custom-theme .el-range-editor--mini .el-range__icon{line-height:20px}.custom-theme .el-range-editor.is-disabled{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-range-editor.is-disabled:focus,.custom-theme .el-range-editor.is-disabled:hover{border-color:#dfe4ed}.custom-theme .el-range-editor.is-disabled input{background-color:#f5f7fa;color:#b4bccc;cursor:not-allowed}.custom-theme .el-range-editor.is-disabled input::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled input:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled input::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled input::placeholder{color:#b4bccc}.custom-theme .el-range-editor.is-disabled .el-range-separator{color:#b4bccc}.custom-theme .el-time-spinner.has-seconds .el-time-spinner__wrapper{width:33.3%}.custom-theme .el-time-spinner.has-seconds .el-time-spinner__wrapper:nth-child(2){margin-left:1%}.custom-theme .el-time-spinner__wrapper{max-height:190px;overflow:auto;display:inline-block;width:50%;vertical-align:top;position:relative}.custom-theme .el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default){padding-bottom:15px}.custom-theme .el-time-spinner__wrapper.is-arrow{-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center;overflow:hidden}.custom-theme .el-time-spinner__wrapper.is-arrow .el-time-spinner__list{-webkit-transform:translateY(-32px);transform:translateY(-32px)}.custom-theme .el-time-spinner__wrapper.is-arrow .el-time-spinner__item:hover:not(.disabled):not(.active){background:#fff;cursor:default}.custom-theme .el-time-spinner__arrow{font-size:12px;color:#878d99;position:absolute;left:0;width:100%;z-index:1;text-align:center;height:30px;line-height:30px;cursor:pointer}.custom-theme .el-time-spinner__arrow:hover{color:#262729}.custom-theme .el-time-spinner__arrow.el-icon-arrow-up{top:10px}.custom-theme .el-time-spinner__arrow.el-icon-arrow-down{bottom:10px}.custom-theme .el-time-spinner__input.el-input{width:70%}.custom-theme .el-time-spinner__input.el-input .el-input__inner{padding:0;text-align:center}.custom-theme .el-time-spinner__list{padding:0;margin:0;list-style:none;text-align:center}.custom-theme .el-time-spinner__list::after,.custom-theme .el-time-spinner__list::before{content:'';display:block;width:100%;height:80px}.custom-theme .el-time-spinner__item{height:32px;line-height:32px;font-size:12px;color:#5a5e66}.custom-theme .el-time-spinner__item:hover:not(.disabled):not(.active){background:#f5f7fa;cursor:pointer}.custom-theme .el-time-spinner__item.active:not(.disabled){color:#2d2f33;font-weight:700}.custom-theme .el-time-spinner__item.disabled{color:#b4bccc;cursor:not-allowed}.custom-theme .el-time-panel{margin:5px 0;border:solid 1px #dfe4ed;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:2px;position:absolute;width:180px;left:0;z-index:1000;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-theme .el-time-panel__content{font-size:0;position:relative;overflow:hidden}.custom-theme .el-time-panel__content::after,.custom-theme .el-time-panel__content::before{content:\"\";top:50%;position:absolute;margin-top:-15px;height:32px;z-index:-1;left:0;right:0;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;text-align:left;border-top:1px solid #dfe4ed;border-bottom:1px solid #dfe4ed}.custom-theme .el-time-panel__content::after{left:50%;margin-left:12%;margin-right:12%}.custom-theme .el-time-panel__content::before{padding-left:50%;margin-right:12%;margin-left:12%}.custom-theme .el-time-panel__content.has-seconds::after{left:calc(100% / 3 * 2)}.custom-theme .el-time-panel__content.has-seconds::before{padding-left:calc(100% / 3)}.custom-theme .el-time-panel__footer{border-top:1px solid #e4e4e4;padding:4px;height:36px;line-height:25px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-time-panel__btn{border:none;line-height:28px;padding:0 5px;margin:0 5px;cursor:pointer;background-color:transparent;outline:0;font-size:12px;color:#2d2f33}.custom-theme .el-time-panel__btn.confirm{font-weight:800;color:#262729}.custom-theme .el-time-panel .popper__arrow{-webkit-transform:translateX(-400%);transform:translateX(-400%)}.custom-theme .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.custom-theme .el-input::-webkit-scrollbar{z-index:11;width:6px}.custom-theme .el-input::-webkit-scrollbar:horizontal{height:6px}.custom-theme .el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.custom-theme .el-input::-webkit-scrollbar-corner{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.custom-theme .el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.custom-theme .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input__inner:hover{border-color:#b4bccc}.custom-theme .el-input__inner:focus{outline:0;border-color:#262729}.custom-theme .el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.custom-theme .el-input__suffix-inner{pointer-events:all}.custom-theme .el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s}.custom-theme .el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.custom-theme .el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.custom-theme .el-input__validateIcon{pointer-events:none}.custom-theme .el-input.is-active .el-input__inner{outline:0;border-color:#262729}.custom-theme .el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__icon{cursor:not-allowed}.custom-theme .el-input--suffix .el-input__inner{padding-right:30px}.custom-theme .el-input--prefix .el-input__inner{padding-left:30px}.custom-theme .el-input--medium{font-size:14px}.custom-theme .el-input--medium .el-input__inner{height:36px}.custom-theme .el-input--medium .el-input__icon{line-height:36px}.custom-theme .el-input--small{font-size:13px}.custom-theme .el-input--small .el-input__inner{height:32px}.custom-theme .el-input--small .el-input__icon{line-height:32px}.custom-theme .el-input--mini{font-size:12px}.custom-theme .el-input--mini .el-input__inner{height:28px}.custom-theme .el-input--mini .el-input__icon{line-height:28px}.custom-theme .el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.custom-theme .el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.custom-theme .el-input-group__append,.custom-theme .el-input-group__prepend{background-color:#f5f7fa;color:#0a76a4;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.custom-theme .el-input-group__append:focus,.custom-theme .el-input-group__prepend:focus{outline:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-select,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-select{display:inline-block;margin:-20px}.custom-theme .el-input-group__append button.el-button,.custom-theme .el-input-group__append div.el-select .el-input__inner,.custom-theme .el-input-group__append div.el-select:hover .el-input__inner,.custom-theme .el-input-group__prepend button.el-button,.custom-theme .el-input-group__prepend div.el-select .el-input__inner,.custom-theme .el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-input,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-input{font-size:inherit}.custom-theme .el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-textarea{display:inline-block;width:100%;vertical-align:bottom}.custom-theme .el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:hover{border-color:#b4bccc}.custom-theme .el-textarea__inner:focus{outline:0;border-color:#262729}.custom-theme .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-scrollbar{overflow:hidden;position:relative}.custom-theme .el-scrollbar:active>.el-scrollbar__bar,.custom-theme .el-scrollbar:focus>.el-scrollbar__bar,.custom-theme .el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity 340ms ease-out;transition:opacity 340ms ease-out}.custom-theme .el-scrollbar__wrap{overflow:scroll;height:100%}.custom-theme .el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.custom-theme .el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(135,141,153,.3);-webkit-transition:.3s background-color;transition:.3s background-color}.custom-theme .el-scrollbar__thumb:hover{background-color:rgba(135,141,153,.5)}.custom-theme .el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity 120ms ease-out;transition:opacity 120ms ease-out}.custom-theme .el-scrollbar__bar.is-vertical{width:6px;top:2px}.custom-theme .el-scrollbar__bar.is-vertical>div{width:100%}.custom-theme .el-scrollbar__bar.is-horizontal{height:6px;left:2px}.custom-theme .el-scrollbar__bar.is-horizontal>div{height:100%}.custom-theme .el-popper .popper__arrow,.custom-theme .el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.custom-theme .el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.custom-theme .el-popper .popper__arrow::after{content:\" \";border-width:6px}.custom-theme .el-popper[x-placement^=top]{margin-bottom:12px}.custom-theme .el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.custom-theme .el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.custom-theme .el-popper[x-placement^=bottom]{margin-top:12px}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.custom-theme .el-popper[x-placement^=right]{margin-left:12px}.custom-theme .el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.custom-theme .el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.custom-theme .el-popper[x-placement^=left]{margin-right:12px}.custom-theme .el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.custom-theme .el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.custom-theme .el-popper .popper__arrow,.custom-theme .el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.custom-theme .el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.custom-theme .el-popper .popper__arrow::after{content:\" \";border-width:6px}.custom-theme .el-popper[x-placement^=top]{margin-bottom:12px}.custom-theme .el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.custom-theme .el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.custom-theme .el-popper[x-placement^=bottom]{margin-top:12px}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.custom-theme .el-popper[x-placement^=right]{margin-left:12px}.custom-theme .el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.custom-theme .el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.custom-theme .el-popper[x-placement^=left]{margin-right:12px}.custom-theme .el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.custom-theme .el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.custom-theme .el-popover{position:absolute;background:#fff;min-width:150px;border-radius:4px;border:1px solid #e6ebf5;padding:12px;z-index:2000;color:#5a5e66;line-height:1.4;text-align:justify;word-break:break-all;font-size:14px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.custom-theme .el-popover--plain{padding:18px 20px}.custom-theme .el-popover__title{color:#2d2f33;font-size:16px;line-height:1;margin-bottom:12px}.custom-theme .el-tooltip__popper{position:absolute;border-radius:4px;padding:10px;z-index:2000;font-size:12px;line-height:1.2}.custom-theme .el-tooltip__popper .popper__arrow,.custom-theme .el-tooltip__popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.custom-theme .el-tooltip__popper .popper__arrow{border-width:6px}.custom-theme .el-tooltip__popper .popper__arrow::after{content:\" \";border-width:5px}.custom-theme .el-tooltip__popper[x-placement^=top]{margin-bottom:12px}.custom-theme .el-tooltip__popper[x-placement^=top] .popper__arrow{bottom:-6px;border-top-color:#2d2f33;border-bottom-width:0}.custom-theme .el-tooltip__popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-5px;border-top-color:#2d2f33;border-bottom-width:0}.custom-theme .el-tooltip__popper[x-placement^=bottom]{margin-top:12px}.custom-theme .el-tooltip__popper[x-placement^=bottom] .popper__arrow{top:-6px;border-top-width:0;border-bottom-color:#2d2f33}.custom-theme .el-tooltip__popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-5px;border-top-width:0;border-bottom-color:#2d2f33}.custom-theme .el-tooltip__popper[x-placement^=right]{margin-left:12px}.custom-theme .el-tooltip__popper[x-placement^=right] .popper__arrow{left:-6px;border-right-color:#2d2f33;border-left-width:0}.custom-theme .el-tooltip__popper[x-placement^=right] .popper__arrow::after{bottom:-5px;left:1px;border-right-color:#2d2f33;border-left-width:0}.custom-theme .el-tooltip__popper[x-placement^=left]{margin-right:12px}.custom-theme .el-tooltip__popper[x-placement^=left] .popper__arrow{right:-6px;border-right-width:0;border-left-color:#2d2f33}.custom-theme .el-tooltip__popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-5px;margin-left:-5px;border-right-width:0;border-left-color:#2d2f33}.custom-theme .el-tooltip__popper.is-dark{background:#2d2f33;color:#fff}.custom-theme .el-tooltip__popper.is-light{background:#fff;border:1px solid #2d2f33}.custom-theme .el-tooltip__popper.is-light[x-placement^=top] .popper__arrow{border-top-color:#2d2f33}.custom-theme .el-tooltip__popper.is-light[x-placement^=top] .popper__arrow::after{border-top-color:#fff}.custom-theme .el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow{border-bottom-color:#2d2f33}.custom-theme .el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow::after{border-bottom-color:#fff}.custom-theme .el-tooltip__popper.is-light[x-placement^=left] .popper__arrow{border-left-color:#2d2f33}.custom-theme .el-tooltip__popper.is-light[x-placement^=left] .popper__arrow::after{border-left-color:#fff}.custom-theme .el-tooltip__popper.is-light[x-placement^=right] .popper__arrow{border-right-color:#2d2f33}.custom-theme .el-tooltip__popper.is-light[x-placement^=right] .popper__arrow::after{border-right-color:#fff}.custom-theme .v-modal-enter{-webkit-animation:v-modal-in .2s ease;animation:v-modal-in .2s ease}.custom-theme .v-modal-leave{-webkit-animation:v-modal-out .2s ease forwards;animation:v-modal-out .2s ease forwards}@keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-out{100%{opacity:0}}.custom-theme .v-modal{position:fixed;left:0;top:0;width:100%;height:100%;opacity:.5;background:#000}.custom-theme .el-button{display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;background:#fff;border:1px solid #d8dce5;border-color:#d8dce5;color:#5a5e66;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:.1s;transition:.1s;font-weight:500;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:14px;border-radius:4px}.custom-theme .el-button+.el-button{margin-left:10px}.custom-theme .el-button.is-round{padding:12px 20px}.custom-theme .el-button:focus,.custom-theme .el-button:hover{color:#262729;border-color:#bebebf;background-color:#e9e9ea}.custom-theme .el-button:active{color:#222325;border-color:#222325;outline:0}.custom-theme .el-button::-moz-focus-inner{border:0}.custom-theme .el-button [class*=el-icon-]+span{margin-left:5px}.custom-theme .el-button.is-plain:focus,.custom-theme .el-button.is-plain:hover{background:#fff;border-color:#262729;color:#262729}.custom-theme .el-button.is-plain:active{background:#fff;border-color:#222325;color:#222325;outline:0}.custom-theme .el-button.is-active{color:#222325;border-color:#222325}.custom-theme .el-button.is-disabled,.custom-theme .el-button.is-disabled:focus,.custom-theme .el-button.is-disabled:hover{color:#b4bccc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#e6ebf5}.custom-theme .el-button.is-disabled.el-button--text{background-color:transparent}.custom-theme .el-button.is-disabled.is-plain,.custom-theme .el-button.is-disabled.is-plain:focus,.custom-theme .el-button.is-disabled.is-plain:hover{background-color:#fff;border-color:#e6ebf5;color:#b4bccc}.custom-theme .el-button.is-loading{position:relative;pointer-events:none}.custom-theme .el-button.is-loading:before{pointer-events:none;content:'';position:absolute;left:-1px;top:-1px;right:-1px;bottom:-1px;border-radius:inherit;background-color:rgba(255,255,255,.35)}.custom-theme .el-button.is-round{border-radius:20px;padding:12px 23px}.custom-theme .el-button--primary{color:#fff;background-color:#262729;border-color:#262729}.custom-theme .el-button--primary:focus,.custom-theme .el-button--primary:hover{background:#515254;border-color:#515254;color:#fff}.custom-theme .el-button--primary:active{background:#222325;border-color:#222325;color:#fff;outline:0}.custom-theme .el-button--primary.is-active{background:#222325;border-color:#222325;color:#fff}.custom-theme .el-button--primary.is-disabled,.custom-theme .el-button--primary.is-disabled:active,.custom-theme .el-button--primary.is-disabled:focus,.custom-theme .el-button--primary.is-disabled:hover{color:#fff;background-color:#939394;border-color:#939394}.custom-theme .el-button--primary.is-plain{color:#262729;background:#e9e9ea;border-color:#a8a9a9}.custom-theme .el-button--primary.is-plain:focus,.custom-theme .el-button--primary.is-plain:hover{background:#262729;border-color:#262729;color:#fff}.custom-theme .el-button--primary.is-plain:active{background:#222325;border-color:#222325;color:#fff;outline:0}.custom-theme .el-button--primary.is-plain.is-disabled,.custom-theme .el-button--primary.is-plain.is-disabled:active,.custom-theme .el-button--primary.is-plain.is-disabled:focus,.custom-theme .el-button--primary.is-plain.is-disabled:hover{color:#7d7d7f;background-color:#e9e9ea;border-color:#d4d4d4}.custom-theme .el-button--success{color:#fff;background-color:#409167;border-color:#409167}.custom-theme .el-button--success:focus,.custom-theme .el-button--success:hover{background:#66a785;border-color:#66a785;color:#fff}.custom-theme .el-button--success:active{background:#3a835d;border-color:#3a835d;color:#fff;outline:0}.custom-theme .el-button--success.is-active{background:#3a835d;border-color:#3a835d;color:#fff}.custom-theme .el-button--success.is-disabled,.custom-theme .el-button--success.is-disabled:active,.custom-theme .el-button--success.is-disabled:focus,.custom-theme .el-button--success.is-disabled:hover{color:#fff;background-color:#a0c8b3;border-color:#a0c8b3}.custom-theme .el-button--success.is-plain{color:#409167;background:#ecf4f0;border-color:#b3d3c2}.custom-theme .el-button--success.is-plain:focus,.custom-theme .el-button--success.is-plain:hover{background:#409167;border-color:#409167;color:#fff}.custom-theme .el-button--success.is-plain:active{background:#3a835d;border-color:#3a835d;color:#fff;outline:0}.custom-theme .el-button--success.is-plain.is-disabled,.custom-theme .el-button--success.is-plain.is-disabled:active,.custom-theme .el-button--success.is-plain.is-disabled:focus,.custom-theme .el-button--success.is-plain.is-disabled:hover{color:#8cbda4;background-color:#ecf4f0;border-color:#d9e9e1}.custom-theme .el-button--warning{color:#fff;background-color:#9da408;border-color:#9da408}.custom-theme .el-button--warning:focus,.custom-theme .el-button--warning:hover{background:#b1b639;border-color:#b1b639;color:#fff}.custom-theme .el-button--warning:active{background:#8d9407;border-color:#8d9407;color:#fff;outline:0}.custom-theme .el-button--warning.is-active{background:#8d9407;border-color:#8d9407;color:#fff}.custom-theme .el-button--warning.is-disabled,.custom-theme .el-button--warning.is-disabled:active,.custom-theme .el-button--warning.is-disabled:focus,.custom-theme .el-button--warning.is-disabled:hover{color:#fff;background-color:#ced284;border-color:#ced284}.custom-theme .el-button--warning.is-plain{color:#9da408;background:#f5f6e6;border-color:#d8db9c}.custom-theme .el-button--warning.is-plain:focus,.custom-theme .el-button--warning.is-plain:hover{background:#9da408;border-color:#9da408;color:#fff}.custom-theme .el-button--warning.is-plain:active{background:#8d9407;border-color:#8d9407;color:#fff;outline:0}.custom-theme .el-button--warning.is-plain.is-disabled,.custom-theme .el-button--warning.is-plain.is-disabled:active,.custom-theme .el-button--warning.is-plain.is-disabled:focus,.custom-theme .el-button--warning.is-plain.is-disabled:hover{color:#c4c86b;background-color:#f5f6e6;border-color:#ebedce}.custom-theme .el-button--danger{color:#fff;background-color:#b3450e;border-color:#b3450e}.custom-theme .el-button--danger:focus,.custom-theme .el-button--danger:hover{background:#c26a3e;border-color:#c26a3e;color:#fff}.custom-theme .el-button--danger:active{background:#a13e0d;border-color:#a13e0d;color:#fff;outline:0}.custom-theme .el-button--danger.is-active{background:#a13e0d;border-color:#a13e0d;color:#fff}.custom-theme .el-button--danger.is-disabled,.custom-theme .el-button--danger.is-disabled:active,.custom-theme .el-button--danger.is-disabled:focus,.custom-theme .el-button--danger.is-disabled:hover{color:#fff;background-color:#d9a287;border-color:#d9a287}.custom-theme .el-button--danger.is-plain{color:#b3450e;background:#f7ece7;border-color:#e1b59f}.custom-theme .el-button--danger.is-plain:focus,.custom-theme .el-button--danger.is-plain:hover{background:#b3450e;border-color:#b3450e;color:#fff}.custom-theme .el-button--danger.is-plain:active{background:#a13e0d;border-color:#a13e0d;color:#fff;outline:0}.custom-theme .el-button--danger.is-plain.is-disabled,.custom-theme .el-button--danger.is-plain.is-disabled:active,.custom-theme .el-button--danger.is-plain.is-disabled:focus,.custom-theme .el-button--danger.is-plain.is-disabled:hover{color:#d18f6e;background-color:#f7ece7;border-color:#f0dacf}.custom-theme .el-button--info{color:#fff;background-color:#0a76a4;border-color:#0a76a4}.custom-theme .el-button--info:focus,.custom-theme .el-button--info:hover{background:#3b91b6;border-color:#3b91b6;color:#fff}.custom-theme .el-button--info:active{background:#096a94;border-color:#096a94;color:#fff;outline:0}.custom-theme .el-button--info.is-active{background:#096a94;border-color:#096a94;color:#fff}.custom-theme .el-button--info.is-disabled,.custom-theme .el-button--info.is-disabled:active,.custom-theme .el-button--info.is-disabled:focus,.custom-theme .el-button--info.is-disabled:hover{color:#fff;background-color:#85bbd2;border-color:#85bbd2}.custom-theme .el-button--info.is-plain{color:#0a76a4;background:#e7f1f6;border-color:#9dc8db}.custom-theme .el-button--info.is-plain:focus,.custom-theme .el-button--info.is-plain:hover{background:#0a76a4;border-color:#0a76a4;color:#fff}.custom-theme .el-button--info.is-plain:active{background:#096a94;border-color:#096a94;color:#fff;outline:0}.custom-theme .el-button--info.is-plain.is-disabled,.custom-theme .el-button--info.is-plain.is-disabled:active,.custom-theme .el-button--info.is-plain.is-disabled:focus,.custom-theme .el-button--info.is-plain.is-disabled:hover{color:#6cadc8;background-color:#e7f1f6;border-color:#cee4ed}.custom-theme .el-button--medium{padding:10px 20px;font-size:14px;border-radius:4px}.custom-theme .el-button--medium.is-round{padding:10px 20px}.custom-theme .el-button--small{padding:9px 15px;font-size:12px;border-radius:3px}.custom-theme .el-button--small.is-round{padding:9px 15px}.custom-theme .el-button--mini{padding:7px 15px;font-size:12px;border-radius:3px}.custom-theme .el-button--mini.is-round{padding:7px 15px}.custom-theme .el-button--text{border:none;color:#262729;background:0 0;padding-left:0;padding-right:0}.custom-theme .el-button--text:focus,.custom-theme .el-button--text:hover{color:#515254;border-color:transparent;background-color:transparent}.custom-theme .el-button--text:active{color:#222325;border-color:transparent;background-color:transparent}.custom-theme .el-button-group{display:inline-block;vertical-align:middle}.custom-theme .el-button-group::after,.custom-theme .el-button-group::before{display:table;content:\"\"}.custom-theme .el-button-group::after{clear:both}.custom-theme .el-button-group .el-button{float:left;position:relative}.custom-theme .el-button-group .el-button+.el-button{margin-left:0}.custom-theme .el-button-group .el-button:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-button-group .el-button:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-button-group .el-button:not(:first-child):not(:last-child){border-radius:0}.custom-theme .el-button-group .el-button:not(:last-child){margin-right:-1px}.custom-theme .el-button-group .el-button:active,.custom-theme .el-button-group .el-button:focus,.custom-theme .el-button-group .el-button:hover{z-index:1}.custom-theme .el-button-group .el-button.is-active{z-index:1}.custom-theme .el-button-group .el-button--primary:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--primary:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--primary:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.custom-theme .el-input::-webkit-scrollbar{z-index:11;width:6px}.custom-theme .el-input::-webkit-scrollbar:horizontal{height:6px}.custom-theme .el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.custom-theme .el-input::-webkit-scrollbar-corner{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.custom-theme .el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.custom-theme .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input__inner:hover{border-color:#b4bccc}.custom-theme .el-input__inner:focus{outline:0;border-color:#262729}.custom-theme .el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.custom-theme .el-input__suffix-inner{pointer-events:all}.custom-theme .el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s}.custom-theme .el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.custom-theme .el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.custom-theme .el-input__validateIcon{pointer-events:none}.custom-theme .el-input.is-active .el-input__inner{outline:0;border-color:#262729}.custom-theme .el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__icon{cursor:not-allowed}.custom-theme .el-input--suffix .el-input__inner{padding-right:30px}.custom-theme .el-input--prefix .el-input__inner{padding-left:30px}.custom-theme .el-input--medium{font-size:14px}.custom-theme .el-input--medium .el-input__inner{height:36px}.custom-theme .el-input--medium .el-input__icon{line-height:36px}.custom-theme .el-input--small{font-size:13px}.custom-theme .el-input--small .el-input__inner{height:32px}.custom-theme .el-input--small .el-input__icon{line-height:32px}.custom-theme .el-input--mini{font-size:12px}.custom-theme .el-input--mini .el-input__inner{height:28px}.custom-theme .el-input--mini .el-input__icon{line-height:28px}.custom-theme .el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.custom-theme .el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.custom-theme .el-input-group__append,.custom-theme .el-input-group__prepend{background-color:#f5f7fa;color:#0a76a4;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.custom-theme .el-input-group__append:focus,.custom-theme .el-input-group__prepend:focus{outline:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-select,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-select{display:inline-block;margin:-20px}.custom-theme .el-input-group__append button.el-button,.custom-theme .el-input-group__append div.el-select .el-input__inner,.custom-theme .el-input-group__append div.el-select:hover .el-input__inner,.custom-theme .el-input-group__prepend button.el-button,.custom-theme .el-input-group__prepend div.el-select .el-input__inner,.custom-theme .el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-input,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-input{font-size:inherit}.custom-theme .el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-textarea{display:inline-block;width:100%;vertical-align:bottom}.custom-theme .el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:hover{border-color:#b4bccc}.custom-theme .el-textarea__inner:focus{outline:0;border-color:#262729}.custom-theme .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-message-box{display:inline-block;width:420px;padding-bottom:10px;vertical-align:middle;background-color:#fff;border-radius:4px;border:1px solid #e6ebf5;font-size:18px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);text-align:left;overflow:hidden;-webkit-backface-visibility:hidden;backface-visibility:hidden}.custom-theme .el-message-box__wrapper{position:fixed;top:0;bottom:0;left:0;right:0;text-align:center}.custom-theme .el-message-box__wrapper::after{content:\"\";display:inline-block;height:100%;width:0;vertical-align:middle}.custom-theme .el-message-box__header{position:relative;padding:15px;padding-bottom:10px}.custom-theme .el-message-box__title{padding-left:0;margin-bottom:0;font-size:18px;line-height:1;color:#2d2f33}.custom-theme .el-message-box__headerbtn{position:absolute;top:15px;right:15px;padding:0;border:none;outline:0;background:0 0;font-size:16px;cursor:pointer}.custom-theme .el-message-box__headerbtn .el-message-box__close{color:#0a76a4}.custom-theme .el-message-box__headerbtn:focus .el-message-box__close,.custom-theme .el-message-box__headerbtn:hover .el-message-box__close{color:#262729}.custom-theme .el-message-box__content{position:relative;padding:10px 15px;color:#5a5e66;font-size:14px}.custom-theme .el-message-box__input{padding-top:15px}.custom-theme .el-message-box__input input.invalid{border-color:#b3450e}.custom-theme .el-message-box__input input.invalid:focus{border-color:#b3450e}.custom-theme .el-message-box__status{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);font-size:24px!important}.custom-theme .el-message-box__status::before{padding-left:1px}.custom-theme .el-message-box__status+.el-message-box__message{padding-left:36px;padding-right:12px}.custom-theme .el-message-box__status.el-icon-success{color:#409167}.custom-theme .el-message-box__status.el-icon-info{color:#0a76a4}.custom-theme .el-message-box__status.el-icon-warning{color:#9da408}.custom-theme .el-message-box__status.el-icon-error{color:#b3450e}.custom-theme .el-message-box__message{margin:0}.custom-theme .el-message-box__message p{margin:0;line-height:24px}.custom-theme .el-message-box__errormsg{color:#b3450e;font-size:12px;min-height:18px;margin-top:2px}.custom-theme .el-message-box__btns{padding:5px 15px 0;text-align:right}.custom-theme .el-message-box__btns button:nth-child(2){margin-left:10px}.custom-theme .el-message-box__btns-reverse{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.custom-theme .el-message-box--center{padding-bottom:30px}.custom-theme .el-message-box--center .el-message-box__header{padding-top:30px}.custom-theme .el-message-box--center .el-message-box__title{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.custom-theme .el-message-box--center .el-message-box__status{position:relative;top:auto;padding-right:5px;text-align:center;-webkit-transform:translateY(-1px);transform:translateY(-1px)}.custom-theme .el-message-box--center .el-message-box__message{margin-left:0}.custom-theme .el-message-box--center .el-message-box__btns,.custom-theme .el-message-box--center .el-message-box__content{text-align:center}.custom-theme .el-message-box--center .el-message-box__content{padding-left:27px;padding-right:27px}.custom-theme .msgbox-fade-enter-active{-webkit-animation:msgbox-fade-in .3s;animation:msgbox-fade-in .3s}.custom-theme .msgbox-fade-leave-active{-webkit-animation:msgbox-fade-out .3s;animation:msgbox-fade-out .3s}@-webkit-keyframes msgbox-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes msgbox-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes msgbox-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes msgbox-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.custom-theme .el-breadcrumb{font-size:14px;line-height:1}.custom-theme .el-breadcrumb::after,.custom-theme .el-breadcrumb::before{display:table;content:\"\"}.custom-theme .el-breadcrumb::after{clear:both}.custom-theme .el-breadcrumb__separator{margin:0 9px;font-weight:700;color:#b4bccc}.custom-theme .el-breadcrumb__separator[class*=icon]{margin:0 6px;font-weight:400}.custom-theme .el-breadcrumb__item{float:left}.custom-theme .el-breadcrumb__inner,.custom-theme .el-breadcrumb__inner a{font-weight:700;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1);color:#2d2f33}.custom-theme .el-breadcrumb__inner a:hover,.custom-theme .el-breadcrumb__inner:hover{color:#262729;cursor:pointer}.custom-theme .el-breadcrumb__item:last-child .el-breadcrumb__inner,.custom-theme .el-breadcrumb__item:last-child .el-breadcrumb__inner a,.custom-theme .el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover,.custom-theme .el-breadcrumb__item:last-child .el-breadcrumb__inner:hover{font-weight:400;color:#5a5e66;cursor:text}.custom-theme .el-breadcrumb__item:last-child .el-breadcrumb__separator{display:none}.custom-theme .el-form--label-left .el-form-item__label{text-align:left}.custom-theme .el-form--label-top .el-form-item__label{float:none;display:inline-block;text-align:left;padding:0 0 10px 0}.custom-theme .el-form--inline .el-form-item{display:inline-block;margin-right:10px;vertical-align:top}.custom-theme .el-form--inline .el-form-item__label{float:none;display:inline-block}.custom-theme .el-form--inline .el-form-item__content{display:inline-block;vertical-align:top}.custom-theme .el-form--inline.el-form--label-top .el-form-item__content{display:block}.custom-theme .el-form-item{margin-bottom:22px}.custom-theme .el-form-item::after,.custom-theme .el-form-item::before{display:table;content:\"\"}.custom-theme .el-form-item::after{clear:both}.custom-theme .el-form-item .el-form-item{margin-bottom:0}.custom-theme .el-form-item .el-input__validateIcon{display:none}.custom-theme .el-form-item--medium .el-form-item__label{line-height:36px}.custom-theme .el-form-item--medium .el-form-item__content{line-height:36px}.custom-theme .el-form-item--small .el-form-item__label{line-height:32px}.custom-theme .el-form-item--small .el-form-item__content{line-height:32px}.custom-theme .el-form-item--small.el-form-item{margin-bottom:18px}.custom-theme .el-form-item--small .el-form-item__error{padding-top:2px}.custom-theme .el-form-item--mini .el-form-item__label{line-height:28px}.custom-theme .el-form-item--mini .el-form-item__content{line-height:28px}.custom-theme .el-form-item--mini.el-form-item{margin-bottom:18px}.custom-theme .el-form-item--mini .el-form-item__error{padding-top:1px}.custom-theme .el-form-item__label{text-align:right;vertical-align:middle;float:left;font-size:14px;color:#5a5e66;line-height:40px;padding:0 12px 0 0;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-form-item__content{line-height:40px;position:relative;font-size:14px}.custom-theme .el-form-item__content::after,.custom-theme .el-form-item__content::before{display:table;content:\"\"}.custom-theme .el-form-item__content::after{clear:both}.custom-theme .el-form-item__error{color:#b3450e;font-size:12px;line-height:1;padding-top:4px;position:absolute;top:100%;left:0}.custom-theme .el-form-item__error--inline{position:relative;top:auto;left:auto;display:inline-block;margin-left:10px}.custom-theme .el-form-item.is-required .el-form-item__label:before{content:'*';color:#b3450e;margin-right:4px}.custom-theme .el-form-item.is-error .el-input__inner,.custom-theme .el-form-item.is-error .el-input__inner:focus,.custom-theme .el-form-item.is-error .el-textarea__inner,.custom-theme .el-form-item.is-error .el-textarea__inner:focus{border-color:#b3450e}.custom-theme .el-form-item.is-error .el-input-group__append .el-input__inner,.custom-theme .el-form-item.is-error .el-input-group__prepend .el-input__inner{border-color:transparent}.custom-theme .el-form-item.is-error .el-input__validateIcon{color:#b3450e}.custom-theme .el-form-item.is-success .el-input__inner,.custom-theme .el-form-item.is-success .el-input__inner:focus,.custom-theme .el-form-item.is-success .el-textarea__inner,.custom-theme .el-form-item.is-success .el-textarea__inner:focus{border-color:#409167}.custom-theme .el-form-item.is-success .el-input-group__append .el-input__inner,.custom-theme .el-form-item.is-success .el-input-group__prepend .el-input__inner{border-color:transparent}.custom-theme .el-form-item.is-success .el-input__validateIcon{color:#409167}.custom-theme .el-form-item--feedback .el-input__validateIcon{display:inline-block}.custom-theme .el-tabs__header{padding:0;position:relative;margin:0 0 15px}.custom-theme .el-tabs__active-bar{position:absolute;bottom:0;left:0;height:2px;background-color:#262729;z-index:1;-webkit-transition:-webkit-transform .3s cubic-bezier(.645,.045,.355,1);transition:-webkit-transform .3s cubic-bezier(.645,.045,.355,1);transition:transform .3s cubic-bezier(.645,.045,.355,1);transition:transform .3s cubic-bezier(.645,.045,.355,1),-webkit-transform .3s cubic-bezier(.645,.045,.355,1);list-style:none}.custom-theme .el-tabs__new-tab{float:right;border:1px solid #d3dce6;height:18px;width:18px;line-height:18px;margin:12px 0 9px 10px;border-radius:3px;text-align:center;font-size:12px;color:#d3dce6;cursor:pointer;-webkit-transition:all .15s;transition:all .15s}.custom-theme .el-tabs__new-tab .el-icon-plus{-webkit-transform:scale(.8,.8);transform:scale(.8,.8)}.custom-theme .el-tabs__new-tab:hover{color:#262729}.custom-theme .el-tabs__nav-wrap{overflow:hidden;margin-bottom:-1px;position:relative}.custom-theme .el-tabs__nav-wrap::after{content:\"\";position:absolute;left:0;bottom:0;width:100%;height:2px;background-color:#dfe4ed;z-index:1}.custom-theme .el-tabs__nav-wrap.is-scrollable{padding:0 20px;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-tabs__nav-scroll{overflow:hidden}.custom-theme .el-tabs__nav-next,.custom-theme .el-tabs__nav-prev{position:absolute;cursor:pointer;line-height:44px;font-size:12px;color:#878d99}.custom-theme .el-tabs__nav-next{right:0}.custom-theme .el-tabs__nav-prev{left:0}.custom-theme .el-tabs__nav{white-space:nowrap;position:relative;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;float:left;z-index:2}.custom-theme .el-tabs__item{padding:0 20px;height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;line-height:40px;display:inline-block;list-style:none;font-size:14px;font-weight:500;color:#2d2f33;position:relative}.custom-theme .el-tabs__item:focus,.custom-theme .el-tabs__item:focus:active{outline:0}.custom-theme .el-tabs__item .el-icon-close{border-radius:50%;text-align:center;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);margin-left:5px}.custom-theme .el-tabs__item .el-icon-close:before{-webkit-transform:scale(.9);transform:scale(.9);display:inline-block}.custom-theme .el-tabs__item .el-icon-close:hover{background-color:#b4bccc;color:#fff}.custom-theme .el-tabs__item.is-active{color:#262729}.custom-theme .el-tabs__item:hover{color:#262729;cursor:pointer}.custom-theme .el-tabs__item.is-disabled{color:#b4bccc;cursor:default}.custom-theme .el-tabs__content{overflow:hidden;position:relative}.custom-theme .el-tabs--card>.el-tabs__header{border-bottom:1px solid #dfe4ed}.custom-theme .el-tabs--card>.el-tabs__header .el-tabs__nav-wrap::after{content:none}.custom-theme .el-tabs--card>.el-tabs__header .el-tabs__nav{border:1px solid #dfe4ed;border-bottom:none;border-radius:4px 4px 0 0}.custom-theme .el-tabs--card>.el-tabs__header .el-tabs__active-bar{display:none}.custom-theme .el-tabs--card>.el-tabs__header .el-tabs__item .el-icon-close{position:relative;font-size:12px;width:0;height:14px;vertical-align:middle;line-height:15px;overflow:hidden;top:-1px;right:-2px;-webkit-transform-origin:100% 50%;transform-origin:100% 50%}.custom-theme .el-tabs--card>.el-tabs__header .el-tabs__item{border-bottom:1px solid transparent;border-left:1px solid #dfe4ed;-webkit-transition:color .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1);transition:color .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-tabs--card>.el-tabs__header .el-tabs__item:first-child{border-left:none}.custom-theme .el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover{padding-left:13px;padding-right:13px}.custom-theme .el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover .el-icon-close{width:14px}.custom-theme .el-tabs--card>.el-tabs__header .el-tabs__item.is-active{border-bottom-color:#fff}.custom-theme .el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable{padding-left:20px;padding-right:20px}.custom-theme .el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable .el-icon-close{width:14px}.custom-theme .el-tabs--border-card{background:#fff;border:1px solid #d8dce5;-webkit-box-shadow:0 2px 4px 0 rgba(0,0,0,.12),0 0 6px 0 rgba(0,0,0,.04);box-shadow:0 2px 4px 0 rgba(0,0,0,.12),0 0 6px 0 rgba(0,0,0,.04)}.custom-theme .el-tabs--border-card>.el-tabs__content{padding:15px}.custom-theme .el-tabs--border-card>.el-tabs__header{background-color:#f5f7fa;border-bottom:1px solid #dfe4ed;margin:0}.custom-theme .el-tabs--border-card>.el-tabs__header .el-tabs__nav-wrap::after{content:none}.custom-theme .el-tabs--border-card>.el-tabs__header .el-tabs__item{-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);border:1px solid transparent;margin:-1px -1px 0;color:#878d99}.custom-theme .el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active{color:#262729;background-color:#fff;border-right-color:#d8dce5;border-left-color:#d8dce5}.custom-theme .el-tabs--border-card>.el-tabs__header .el-tabs__item:hover{color:#262729}.custom-theme .el-tabs--bottom:not(.el-tabs--border-card):not(.el-tabs--card) .el-tabs__item:nth-child(2),.custom-theme .el-tabs--top:not(.el-tabs--border-card):not(.el-tabs--card) .el-tabs__item:nth-child(2){padding-left:0}.custom-theme .el-tabs--bottom .el-tabs__header{margin-bottom:0;margin-top:10px}.custom-theme .el-tabs--bottom.el-tabs--border-card .el-tabs__header{border-bottom:0;border-top:1px solid #d8dce5}.custom-theme .el-tabs--bottom.el-tabs--border-card .el-tabs__nav-wrap{margin-top:-1px;margin-bottom:0}.custom-theme .el-tabs--bottom.el-tabs--border-card .el-tabs__item{border:1px solid transparent;margin:0 -1px -1px -1px}.custom-theme .el-tabs--left,.custom-theme .el-tabs--right{overflow:hidden}.custom-theme .el-tabs--left .el-tabs__header,.custom-theme .el-tabs--left .el-tabs__nav-scroll,.custom-theme .el-tabs--left .el-tabs__nav-wrap,.custom-theme .el-tabs--right .el-tabs__header,.custom-theme .el-tabs--right .el-tabs__nav-scroll,.custom-theme .el-tabs--right .el-tabs__nav-wrap{height:100%}.custom-theme .el-tabs--left .el-tabs__active-bar,.custom-theme .el-tabs--right .el-tabs__active-bar{top:0;bottom:auto;width:2px;height:auto}.custom-theme .el-tabs--left .el-tabs__nav-wrap,.custom-theme .el-tabs--right .el-tabs__nav-wrap{margin-bottom:0}.custom-theme .el-tabs--left .el-tabs__nav-wrap.is-scrollable,.custom-theme .el-tabs--right .el-tabs__nav-wrap.is-scrollable{padding:30px 0}.custom-theme .el-tabs--left .el-tabs__nav-wrap::after,.custom-theme .el-tabs--right .el-tabs__nav-wrap::after{height:100%;width:2px;bottom:auto;top:0}.custom-theme .el-tabs--left .el-tabs__nav,.custom-theme .el-tabs--right .el-tabs__nav{float:none}.custom-theme .el-tabs--left .el-tabs__item,.custom-theme .el-tabs--right .el-tabs__item{display:block}.custom-theme .el-tabs--left .el-tabs__nav-next,.custom-theme .el-tabs--left .el-tabs__nav-prev,.custom-theme .el-tabs--right .el-tabs__nav-next,.custom-theme .el-tabs--right .el-tabs__nav-prev{height:30px;line-height:30px;width:100%;text-align:center;cursor:pointer}.custom-theme .el-tabs--left .el-tabs__nav-next i,.custom-theme .el-tabs--left .el-tabs__nav-prev i,.custom-theme .el-tabs--right .el-tabs__nav-next i,.custom-theme .el-tabs--right .el-tabs__nav-prev i{-webkit-transform:rotateZ(90deg);transform:rotateZ(90deg)}.custom-theme .el-tabs--left .el-tabs__nav-prev,.custom-theme .el-tabs--right .el-tabs__nav-prev{left:auto;top:0}.custom-theme .el-tabs--left .el-tabs__nav-next,.custom-theme .el-tabs--right .el-tabs__nav-next{right:auto;bottom:0}.custom-theme .el-tabs--left .el-tabs__header{float:left;margin-bottom:0;margin-right:10px}.custom-theme .el-tabs--left .el-tabs__nav-wrap{margin-right:-1px}.custom-theme .el-tabs--left .el-tabs__nav-wrap::after{left:auto;right:0}.custom-theme .el-tabs--left .el-tabs__active-bar{right:0;left:auto}.custom-theme .el-tabs--left .el-tabs__item{text-align:right}.custom-theme .el-tabs--left.el-tabs--card .el-tabs__active-bar{display:none}.custom-theme .el-tabs--left.el-tabs--card .el-tabs__item{border-left:none;border-right:1px solid #dfe4ed;border-bottom:none;border-top:1px solid #dfe4ed}.custom-theme .el-tabs--left.el-tabs--card .el-tabs__item:first-child{border-right:1px solid #dfe4ed;border-top:none}.custom-theme .el-tabs--left.el-tabs--card .el-tabs__item.is-active{border:1px solid #dfe4ed;border-right-color:#fff;border-left:none;border-bottom:none}.custom-theme .el-tabs--left.el-tabs--card .el-tabs__item.is-active:first-child{border-top:none}.custom-theme .el-tabs--left.el-tabs--card .el-tabs__item.is-active:last-child{border-bottom:none}.custom-theme .el-tabs--left.el-tabs--card .el-tabs__nav{border-radius:4px 0 0 4px;border-bottom:1px solid #dfe4ed;border-right:none}.custom-theme .el-tabs--left.el-tabs--card .el-tabs__new-tab{float:none}.custom-theme .el-tabs--left.el-tabs--border-card .el-tabs__header{border-right:1px solid #dfe4ed}.custom-theme .el-tabs--left.el-tabs--border-card .el-tabs__item{border:1px solid transparent;margin:-1px 0 -1px -1px}.custom-theme .el-tabs--left.el-tabs--border-card .el-tabs__item.is-active{border-color:transparent;border-top-color:#d1dbe5;border-bottom-color:#d1dbe5}.custom-theme .el-tabs--right .el-tabs__header{float:right;margin-bottom:0;margin-left:10px}.custom-theme .el-tabs--right .el-tabs__nav-wrap{margin-left:-1px}.custom-theme .el-tabs--right .el-tabs__nav-wrap::after{left:0;right:auto}.custom-theme .el-tabs--right .el-tabs__active-bar{left:0}.custom-theme .el-tabs--right.el-tabs--card .el-tabs__active-bar{display:none}.custom-theme .el-tabs--right.el-tabs--card .el-tabs__item{border-bottom:none;border-top:1px solid #dfe4ed}.custom-theme .el-tabs--right.el-tabs--card .el-tabs__item:first-child{border-left:1px solid #dfe4ed;border-top:none}.custom-theme .el-tabs--right.el-tabs--card .el-tabs__item.is-active{border:1px solid #dfe4ed;border-left-color:#fff;border-right:none;border-bottom:none}.custom-theme .el-tabs--right.el-tabs--card .el-tabs__item.is-active:first-child{border-top:none}.custom-theme .el-tabs--right.el-tabs--card .el-tabs__item.is-active:last-child{border-bottom:none}.custom-theme .el-tabs--right.el-tabs--card .el-tabs__nav{border-radius:0 4px 4px 0;border-bottom:1px solid #dfe4ed;border-left:none}.custom-theme .el-tabs--right.el-tabs--border-card .el-tabs__header{border-left:1px solid #dfe4ed}.custom-theme .el-tabs--right.el-tabs--border-card .el-tabs__item{border:1px solid transparent;margin:-1px -1px -1px 0}.custom-theme .el-tabs--right.el-tabs--border-card .el-tabs__item.is-active{border-color:transparent;border-top-color:#d1dbe5;border-bottom-color:#d1dbe5}.custom-theme .slideInLeft-transition,.custom-theme .slideInRight-transition{display:inline-block}.custom-theme .slideInRight-enter{-webkit-animation:slideInRight-enter .3s;animation:slideInRight-enter .3s}.custom-theme .slideInRight-leave{position:absolute;left:0;right:0;-webkit-animation:slideInRight-leave .3s;animation:slideInRight-leave .3s}.custom-theme .slideInLeft-enter{-webkit-animation:slideInLeft-enter .3s;animation:slideInLeft-enter .3s}.custom-theme .slideInLeft-leave{position:absolute;left:0;right:0;-webkit-animation:slideInLeft-leave .3s;animation:slideInLeft-leave .3s}@-webkit-keyframes slideInRight-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInRight-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes slideInRight-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%);opacity:0}}@keyframes slideInRight-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%);opacity:0}}@-webkit-keyframes slideInLeft-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInLeft-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes slideInLeft-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%);opacity:0}}@keyframes slideInLeft-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%);opacity:0}}.custom-theme .el-tag{background-color:rgba(38,39,41,.1);display:inline-block;padding:0 10px;height:32px;line-height:30px;font-size:12px;color:#262729;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid rgba(38,39,41,.2);white-space:nowrap}.custom-theme .el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:18px;width:18px;line-height:18px;vertical-align:middle;top:-1px;right:-5px;color:#262729}.custom-theme .el-tag .el-icon-close::before{display:block}.custom-theme .el-tag .el-icon-close:hover{background-color:#262729;color:#fff}.custom-theme .el-tag--info{background-color:rgba(10,118,164,.1);border-color:rgba(10,118,164,.2);color:#0a76a4}.custom-theme .el-tag--info.is-hit{border-color:#0a76a4}.custom-theme .el-tag--info .el-tag__close{color:#0a76a4}.custom-theme .el-tag--info .el-tag__close:hover{background-color:#0a76a4;color:#fff}.custom-theme .el-tag--success{background-color:rgba(64,145,103,.1);border-color:rgba(64,145,103,.2);color:#409167}.custom-theme .el-tag--success.is-hit{border-color:#409167}.custom-theme .el-tag--success .el-tag__close{color:#409167}.custom-theme .el-tag--success .el-tag__close:hover{background-color:#409167;color:#fff}.custom-theme .el-tag--warning{background-color:rgba(157,164,8,.1);border-color:rgba(157,164,8,.2);color:#9da408}.custom-theme .el-tag--warning.is-hit{border-color:#9da408}.custom-theme .el-tag--warning .el-tag__close{color:#9da408}.custom-theme .el-tag--warning .el-tag__close:hover{background-color:#9da408;color:#fff}.custom-theme .el-tag--danger{background-color:rgba(179,69,14,.1);border-color:rgba(179,69,14,.2);color:#b3450e}.custom-theme .el-tag--danger.is-hit{border-color:#b3450e}.custom-theme .el-tag--danger .el-tag__close{color:#b3450e}.custom-theme .el-tag--danger .el-tag__close:hover{background-color:#b3450e;color:#fff}.custom-theme .el-tag--medium{height:28px;line-height:26px}.custom-theme .el-tag--medium .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-tag--small{height:24px;padding:0 8px;line-height:22px}.custom-theme .el-tag--small .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-tag--mini{height:20px;padding:0 5px;line-height:19px}.custom-theme .el-tag--mini .el-icon-close{margin-left:-3px;-webkit-transform:scale(.7);transform:scale(.7)}.custom-theme .el-tree{cursor:default;background:#fff;color:#5a5e66}.custom-theme .el-tree__empty-block{position:relative;min-height:60px;text-align:center;width:100%;height:100%}.custom-theme .el-tree__empty-text{position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#623615}.custom-theme .el-tree-node{white-space:nowrap}.custom-theme .el-tree-node__content{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:26px;cursor:pointer}.custom-theme .el-tree-node__content>.el-tree-node__expand-icon{padding:6px}.custom-theme .el-tree-node__content>.el-checkbox{margin-right:8px}.custom-theme .el-tree-node__content:hover{background-color:#f5f7fa}.custom-theme .el-tree-node__expand-icon{cursor:pointer;color:#b4bccc;font-size:12px;-webkit-transform:rotate(0);transform:rotate(0);-webkit-transition:-webkit-transform .3s ease-in-out;transition:-webkit-transform .3s ease-in-out;transition:transform .3s ease-in-out;transition:transform .3s ease-in-out,-webkit-transform .3s ease-in-out}.custom-theme .el-tree-node__expand-icon.expanded{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.custom-theme .el-tree-node__expand-icon.is-leaf{color:transparent;cursor:default}.custom-theme .el-tree-node__label{font-size:14px}.custom-theme .el-tree-node__loading-icon{margin-right:8px;font-size:14px;color:#b4bccc}.custom-theme .el-tree-node>.el-tree-node__children{overflow:hidden;background-color:transparent}.custom-theme .el-tree-node.is-expanded>.el-tree-node__children{display:block}.custom-theme .el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content{background-color:#eee}.custom-theme .el-alert{width:100%;padding:8px 16px;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;position:relative;background-color:#fff;overflow:hidden;opacity:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transition:opacity .2s;transition:opacity .2s}.custom-theme .el-alert.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.custom-theme .el-alert--success{background-color:#ecf4f0;color:#409167}.custom-theme .el-alert--success .el-alert__description{color:#409167}.custom-theme .el-alert--info{background-color:#e7f1f6;color:#0a76a4}.custom-theme .el-alert--info .el-alert__description{color:#0a76a4}.custom-theme .el-alert--warning{background-color:#f5f6e6;color:#9da408}.custom-theme .el-alert--warning .el-alert__description{color:#9da408}.custom-theme .el-alert--error{background-color:#f7ece7;color:#b3450e}.custom-theme .el-alert--error .el-alert__description{color:#b3450e}.custom-theme .el-alert__content{display:table-cell;padding:0 8px}.custom-theme .el-alert__icon{font-size:16px;width:16px}.custom-theme .el-alert__icon.is-big{font-size:28px;width:28px}.custom-theme .el-alert__title{font-size:13px;line-height:18px}.custom-theme .el-alert__title.is-bold{font-weight:700}.custom-theme .el-alert .el-alert__description{font-size:12px;margin:5px 0 0 0}.custom-theme .el-alert__closebtn{font-size:12px;color:#b4bccc;opacity:1;position:absolute;top:12px;right:15px;cursor:pointer}.custom-theme .el-alert__closebtn.is-customed{font-style:normal;font-size:13px;top:9px}.custom-theme .el-alert-fade-enter,.custom-theme .el-alert-fade-leave-active{opacity:0}.custom-theme .el-notification{display:-webkit-box;display:-ms-flexbox;display:flex;width:330px;padding:14px 26px 14px 13px;border-radius:8px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #e6ebf5;position:fixed;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;overflow:hidden}.custom-theme .el-notification.right{right:16px}.custom-theme .el-notification.left{left:16px}.custom-theme .el-notification__group{margin-left:13px}.custom-theme .el-notification__title{font-weight:700;font-size:16px;color:#2d2f33;margin:0}.custom-theme .el-notification__content{font-size:14px;line-height:21px;margin:6px 0 0 0;color:#5a5e66;text-align:justify}.custom-theme .el-notification__content p{margin:0}.custom-theme .el-notification__icon{height:24px;width:24px;font-size:24px;-webkit-transform:translateY(4px);transform:translateY(4px)}.custom-theme .el-notification__closeBtn{position:absolute;top:15px;right:15px;cursor:pointer;color:#878d99;font-size:16px}.custom-theme .el-notification__closeBtn:hover{color:#5a5e66}.custom-theme .el-notification .el-icon-success{color:#409167}.custom-theme .el-notification .el-icon-error{color:#b3450e}.custom-theme .el-notification .el-icon-info{color:#0a76a4}.custom-theme .el-notification .el-icon-warning{color:#9da408}.custom-theme .el-notification-fade-enter.right{right:0;-webkit-transform:translateX(100%);transform:translateX(100%)}.custom-theme .el-notification-fade-enter.left{left:0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}.custom-theme .el-notification-fade-leave-active{opacity:0}.custom-theme .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.custom-theme .el-input::-webkit-scrollbar{z-index:11;width:6px}.custom-theme .el-input::-webkit-scrollbar:horizontal{height:6px}.custom-theme .el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.custom-theme .el-input::-webkit-scrollbar-corner{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.custom-theme .el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.custom-theme .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input__inner:hover{border-color:#b4bccc}.custom-theme .el-input__inner:focus{outline:0;border-color:#262729}.custom-theme .el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.custom-theme .el-input__suffix-inner{pointer-events:all}.custom-theme .el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s}.custom-theme .el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.custom-theme .el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.custom-theme .el-input__validateIcon{pointer-events:none}.custom-theme .el-input.is-active .el-input__inner{outline:0;border-color:#262729}.custom-theme .el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__icon{cursor:not-allowed}.custom-theme .el-input--suffix .el-input__inner{padding-right:30px}.custom-theme .el-input--prefix .el-input__inner{padding-left:30px}.custom-theme .el-input--medium{font-size:14px}.custom-theme .el-input--medium .el-input__inner{height:36px}.custom-theme .el-input--medium .el-input__icon{line-height:36px}.custom-theme .el-input--small{font-size:13px}.custom-theme .el-input--small .el-input__inner{height:32px}.custom-theme .el-input--small .el-input__icon{line-height:32px}.custom-theme .el-input--mini{font-size:12px}.custom-theme .el-input--mini .el-input__inner{height:28px}.custom-theme .el-input--mini .el-input__icon{line-height:28px}.custom-theme .el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.custom-theme .el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.custom-theme .el-input-group__append,.custom-theme .el-input-group__prepend{background-color:#f5f7fa;color:#0a76a4;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.custom-theme .el-input-group__append:focus,.custom-theme .el-input-group__prepend:focus{outline:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-select,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-select{display:inline-block;margin:-20px}.custom-theme .el-input-group__append button.el-button,.custom-theme .el-input-group__append div.el-select .el-input__inner,.custom-theme .el-input-group__append div.el-select:hover .el-input__inner,.custom-theme .el-input-group__prepend button.el-button,.custom-theme .el-input-group__prepend div.el-select .el-input__inner,.custom-theme .el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-input,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-input{font-size:inherit}.custom-theme .el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-textarea{display:inline-block;width:100%;vertical-align:bottom}.custom-theme .el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:hover{border-color:#b4bccc}.custom-theme .el-textarea__inner:focus{outline:0;border-color:#262729}.custom-theme .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-input-number{position:relative;display:inline-block;width:180px;line-height:38px}.custom-theme .el-input-number .el-input{display:block}.custom-theme .el-input-number .el-input__inner{-webkit-appearance:none;padding-left:50px;padding-right:50px;text-align:center}.custom-theme .el-input-number__decrease,.custom-theme .el-input-number__increase{position:absolute;z-index:1;top:1px;width:40px;height:auto;text-align:center;background:#f5f7fa;color:#5a5e66;cursor:pointer;font-size:13px}.custom-theme .el-input-number__decrease:hover,.custom-theme .el-input-number__increase:hover{color:#262729}.custom-theme .el-input-number__decrease:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled),.custom-theme .el-input-number__increase:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled){border-color:#262729}.custom-theme .el-input-number__decrease.is-disabled,.custom-theme .el-input-number__increase.is-disabled{color:#b4bccc;cursor:not-allowed}.custom-theme .el-input-number__increase{right:1px;border-radius:0 4px 4px 0;border-left:1px solid #d8dce5}.custom-theme .el-input-number__decrease{left:1px;border-radius:4px 0 0 4px;border-right:1px solid #d8dce5}.custom-theme .el-input-number.is-disabled .el-input-number__decrease,.custom-theme .el-input-number.is-disabled .el-input-number__increase{border-color:#dfe4ed;color:#dfe4ed}.custom-theme .el-input-number.is-disabled .el-input-number__decrease:hover,.custom-theme .el-input-number.is-disabled .el-input-number__increase:hover{color:#dfe4ed;cursor:not-allowed}.custom-theme .el-input-number--medium{width:200px;line-height:34px}.custom-theme .el-input-number--medium .el-input-number__decrease,.custom-theme .el-input-number--medium .el-input-number__increase{width:36px;font-size:14px}.custom-theme .el-input-number--medium .el-input__inner{padding-left:43px;padding-right:43px}.custom-theme .el-input-number--small{width:130px;line-height:30px}.custom-theme .el-input-number--small .el-input-number__decrease,.custom-theme .el-input-number--small .el-input-number__increase{width:32px;font-size:13px}.custom-theme .el-input-number--small .el-input-number__decrease [class*=el-icon],.custom-theme .el-input-number--small .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.9);transform:scale(.9)}.custom-theme .el-input-number--small .el-input__inner{padding-left:39px;padding-right:39px}.custom-theme .el-input-number--mini{width:130px;line-height:26px}.custom-theme .el-input-number--mini .el-input-number__decrease,.custom-theme .el-input-number--mini .el-input-number__increase{width:28px;font-size:12px}.custom-theme .el-input-number--mini .el-input-number__decrease [class*=el-icon],.custom-theme .el-input-number--mini .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-input-number--mini .el-input__inner{padding-left:35px;padding-right:35px}.custom-theme .el-input-number.is-without-controls .el-input__inner{padding-left:15px;padding-right:15px}.custom-theme .el-input-number.is-controls-right .el-input__inner{padding-left:15px;padding-right:50px}.custom-theme .el-input-number.is-controls-right .el-input-number__decrease,.custom-theme .el-input-number.is-controls-right .el-input-number__increase{height:auto;line-height:19px}.custom-theme .el-input-number.is-controls-right .el-input-number__decrease [class*=el-icon],.custom-theme .el-input-number.is-controls-right .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.custom-theme .el-input-number.is-controls-right .el-input-number__increase{border-radius:0 4px 0 0;border-bottom:1px solid #d8dce5}.custom-theme .el-input-number.is-controls-right .el-input-number__decrease{right:1px;bottom:1px;top:auto;left:auto;border-right:none;border-left:1px solid #d8dce5;border-radius:0 0 4px 0}.custom-theme .el-input-number.is-controls-right[class*=medium] [class*=decrease],.custom-theme .el-input-number.is-controls-right[class*=medium] [class*=increase]{line-height:17px}.custom-theme .el-input-number.is-controls-right[class*=small] [class*=decrease],.custom-theme .el-input-number.is-controls-right[class*=small] [class*=increase]{line-height:15px}.custom-theme .el-input-number.is-controls-right[class*=mini] [class*=decrease],.custom-theme .el-input-number.is-controls-right[class*=mini] [class*=increase]{line-height:13px}.custom-theme .el-tooltip__popper{position:absolute;border-radius:4px;padding:10px;z-index:2000;font-size:12px;line-height:1.2}.custom-theme .el-tooltip__popper .popper__arrow,.custom-theme .el-tooltip__popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.custom-theme .el-tooltip__popper .popper__arrow{border-width:6px}.custom-theme .el-tooltip__popper .popper__arrow::after{content:\" \";border-width:5px}.custom-theme .el-tooltip__popper[x-placement^=top]{margin-bottom:12px}.custom-theme .el-tooltip__popper[x-placement^=top] .popper__arrow{bottom:-6px;border-top-color:#2d2f33;border-bottom-width:0}.custom-theme .el-tooltip__popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-5px;border-top-color:#2d2f33;border-bottom-width:0}.custom-theme .el-tooltip__popper[x-placement^=bottom]{margin-top:12px}.custom-theme .el-tooltip__popper[x-placement^=bottom] .popper__arrow{top:-6px;border-top-width:0;border-bottom-color:#2d2f33}.custom-theme .el-tooltip__popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-5px;border-top-width:0;border-bottom-color:#2d2f33}.custom-theme .el-tooltip__popper[x-placement^=right]{margin-left:12px}.custom-theme .el-tooltip__popper[x-placement^=right] .popper__arrow{left:-6px;border-right-color:#2d2f33;border-left-width:0}.custom-theme .el-tooltip__popper[x-placement^=right] .popper__arrow::after{bottom:-5px;left:1px;border-right-color:#2d2f33;border-left-width:0}.custom-theme .el-tooltip__popper[x-placement^=left]{margin-right:12px}.custom-theme .el-tooltip__popper[x-placement^=left] .popper__arrow{right:-6px;border-right-width:0;border-left-color:#2d2f33}.custom-theme .el-tooltip__popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-5px;margin-left:-5px;border-right-width:0;border-left-color:#2d2f33}.custom-theme .el-tooltip__popper.is-dark{background:#2d2f33;color:#fff}.custom-theme .el-tooltip__popper.is-light{background:#fff;border:1px solid #2d2f33}.custom-theme .el-tooltip__popper.is-light[x-placement^=top] .popper__arrow{border-top-color:#2d2f33}.custom-theme .el-tooltip__popper.is-light[x-placement^=top] .popper__arrow::after{border-top-color:#fff}.custom-theme .el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow{border-bottom-color:#2d2f33}.custom-theme .el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow::after{border-bottom-color:#fff}.custom-theme .el-tooltip__popper.is-light[x-placement^=left] .popper__arrow{border-left-color:#2d2f33}.custom-theme .el-tooltip__popper.is-light[x-placement^=left] .popper__arrow::after{border-left-color:#fff}.custom-theme .el-tooltip__popper.is-light[x-placement^=right] .popper__arrow{border-right-color:#2d2f33}.custom-theme .el-tooltip__popper.is-light[x-placement^=right] .popper__arrow::after{border-right-color:#fff}.custom-theme .el-slider::after,.custom-theme .el-slider::before{display:table;content:\"\"}.custom-theme .el-slider::after{clear:both}.custom-theme .el-slider__runway{width:100%;height:6px;margin:16px 0;background-color:#dfe4ed;border-radius:3px;position:relative;cursor:pointer;vertical-align:middle}.custom-theme .el-slider__runway.show-input{margin-right:160px;width:auto}.custom-theme .el-slider__runway.disabled{cursor:default}.custom-theme .el-slider__runway.disabled .el-slider__bar{background-color:#b4bccc}.custom-theme .el-slider__runway.disabled .el-slider__button{border-color:#b4bccc}.custom-theme .el-slider__runway.disabled .el-slider__button-wrapper.hover,.custom-theme .el-slider__runway.disabled .el-slider__button-wrapper:hover{cursor:not-allowed}.custom-theme .el-slider__runway.disabled .el-slider__button-wrapper.dragging{cursor:not-allowed}.custom-theme .el-slider__runway.disabled .el-slider__button.dragging,.custom-theme .el-slider__runway.disabled .el-slider__button.hover,.custom-theme .el-slider__runway.disabled .el-slider__button:hover{-webkit-transform:scale(1);transform:scale(1)}.custom-theme .el-slider__runway.disabled .el-slider__button.hover,.custom-theme .el-slider__runway.disabled .el-slider__button:hover{cursor:not-allowed}.custom-theme .el-slider__runway.disabled .el-slider__button.dragging{cursor:not-allowed}.custom-theme .el-slider__input{float:right;margin-top:3px}.custom-theme .el-slider__bar{height:6px;background-color:#262729;border-top-left-radius:3px;border-bottom-left-radius:3px;position:absolute}.custom-theme .el-slider__button-wrapper{height:36px;width:36px;position:absolute;z-index:1001;top:-15px;-webkit-transform:translateX(-50%);transform:translateX(-50%);background-color:transparent;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-theme .el-slider__button-wrapper::after{display:inline-block;content:\"\";height:100%;vertical-align:middle}.custom-theme .el-slider__button-wrapper .el-tooltip{vertical-align:middle;display:inline-block}.custom-theme .el-slider__button-wrapper.hover,.custom-theme .el-slider__button-wrapper:hover{cursor:-webkit-grab;cursor:grab}.custom-theme .el-slider__button-wrapper.dragging{cursor:-webkit-grabbing;cursor:grabbing}.custom-theme .el-slider__button{width:16px;height:16px;border:solid 2px #262729;background-color:#fff;border-radius:50%;-webkit-transition:.2s;transition:.2s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-theme .el-slider__button.dragging,.custom-theme .el-slider__button.hover,.custom-theme .el-slider__button:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.custom-theme .el-slider__button.hover,.custom-theme .el-slider__button:hover{cursor:-webkit-grab;cursor:grab}.custom-theme .el-slider__button.dragging{cursor:-webkit-grabbing;cursor:grabbing}.custom-theme .el-slider__stop{position:absolute;height:6px;width:6px;border-radius:100%;background-color:#fff;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.custom-theme .el-slider.is-vertical{position:relative}.custom-theme .el-slider.is-vertical .el-slider__runway{width:4px;height:100%;margin:0 16px}.custom-theme .el-slider.is-vertical .el-slider__bar{width:4px;height:auto;border-radius:0 0 3px 3px}.custom-theme .el-slider.is-vertical .el-slider__button-wrapper{top:auto;left:-15px;-webkit-transform:translateY(50%);transform:translateY(50%)}.custom-theme .el-slider.is-vertical .el-slider__stop{-webkit-transform:translateY(50%);transform:translateY(50%)}.custom-theme .el-slider.is-vertical.el-slider--with-input{padding-bottom:58px}.custom-theme .el-slider.is-vertical.el-slider--with-input .el-slider__input{overflow:visible;float:none;position:absolute;bottom:22px;width:36px;margin-top:15px}.custom-theme .el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input__inner{text-align:center;padding-left:5px;padding-right:5px}.custom-theme .el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease,.custom-theme .el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{top:32px;margin-top:-1px;border:1px solid #d8dce5;line-height:20px;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease{width:18px;right:18px;border-bottom-left-radius:4px}.custom-theme .el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{width:19px;border-bottom-right-radius:4px}.custom-theme .el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase~.el-input .el-input__inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.custom-theme .el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__decrease,.custom-theme .el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__increase{border-color:#b4bccc}.custom-theme .el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__decrease,.custom-theme .el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__increase{border-color:#262729}.custom-theme .el-loading-parent--relative{position:relative!important}.custom-theme .el-loading-parent--hidden{overflow:hidden!important}.custom-theme .el-loading-mask{position:absolute;z-index:10000;background-color:rgba(255,255,255,.9);margin:0;top:0;right:0;bottom:0;left:0;-webkit-transition:opacity .3s;transition:opacity .3s}.custom-theme .el-loading-mask.is-fullscreen{position:fixed}.custom-theme .el-loading-mask.is-fullscreen .el-loading-spinner{margin-top:-25px}.custom-theme .el-loading-mask.is-fullscreen .el-loading-spinner .circular{height:50px;width:50px}.custom-theme .el-loading-spinner{top:50%;margin-top:-21px;width:100%;text-align:center;position:absolute}.custom-theme .el-loading-spinner .el-loading-text{color:#262729;margin:3px 0;font-size:14px}.custom-theme .el-loading-spinner .circular{height:42px;width:42px;-webkit-animation:loading-rotate 2s linear infinite;animation:loading-rotate 2s linear infinite}.custom-theme .el-loading-spinner .path{-webkit-animation:loading-dash 1.5s ease-in-out infinite;animation:loading-dash 1.5s ease-in-out infinite;stroke-dasharray:90,150;stroke-dashoffset:0;stroke-width:2;stroke:#262729;stroke-linecap:round}.custom-theme .el-loading-spinner i{color:#262729}.custom-theme .el-loading-fade-enter,.custom-theme .el-loading-fade-leave-active{opacity:0}@-webkit-keyframes loading-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}100%{stroke-dasharray:90,150;stroke-dashoffset:-120px}}@keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}100%{stroke-dasharray:90,150;stroke-dashoffset:-120px}}.custom-theme .el-row{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-row::after,.custom-theme .el-row::before{display:table;content:\"\"}.custom-theme .el-row::after{clear:both}.custom-theme .el-row--flex{display:-webkit-box;display:-ms-flexbox;display:flex}.custom-theme .el-row--flex:after,.custom-theme .el-row--flex:before{display:none}.custom-theme .el-row--flex.is-justify-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.custom-theme .el-row--flex.is-justify-end{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.custom-theme .el-row--flex.is-justify-space-between{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.custom-theme .el-row--flex.is-justify-space-around{-ms-flex-pack:distribute;justify-content:space-around}.custom-theme .el-row--flex.is-align-middle{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.custom-theme .el-row--flex.is-align-bottom{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}.custom-theme [class*=el-col-]{float:left;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-col-0{display:none}.custom-theme .el-col-1{width:4.16667%}.custom-theme .el-col-offset-1{margin-left:4.16667%}.custom-theme .el-col-pull-1{position:relative;right:4.16667%}.custom-theme .el-col-push-1{position:relative;left:4.16667%}.custom-theme .el-col-2{width:8.33333%}.custom-theme .el-col-offset-2{margin-left:8.33333%}.custom-theme .el-col-pull-2{position:relative;right:8.33333%}.custom-theme .el-col-push-2{position:relative;left:8.33333%}.custom-theme .el-col-3{width:12.5%}.custom-theme .el-col-offset-3{margin-left:12.5%}.custom-theme .el-col-pull-3{position:relative;right:12.5%}.custom-theme .el-col-push-3{position:relative;left:12.5%}.custom-theme .el-col-4{width:16.66667%}.custom-theme .el-col-offset-4{margin-left:16.66667%}.custom-theme .el-col-pull-4{position:relative;right:16.66667%}.custom-theme .el-col-push-4{position:relative;left:16.66667%}.custom-theme .el-col-5{width:20.83333%}.custom-theme .el-col-offset-5{margin-left:20.83333%}.custom-theme .el-col-pull-5{position:relative;right:20.83333%}.custom-theme .el-col-push-5{position:relative;left:20.83333%}.custom-theme .el-col-6{width:25%}.custom-theme .el-col-offset-6{margin-left:25%}.custom-theme .el-col-pull-6{position:relative;right:25%}.custom-theme .el-col-push-6{position:relative;left:25%}.custom-theme .el-col-7{width:29.16667%}.custom-theme .el-col-offset-7{margin-left:29.16667%}.custom-theme .el-col-pull-7{position:relative;right:29.16667%}.custom-theme .el-col-push-7{position:relative;left:29.16667%}.custom-theme .el-col-8{width:33.33333%}.custom-theme .el-col-offset-8{margin-left:33.33333%}.custom-theme .el-col-pull-8{position:relative;right:33.33333%}.custom-theme .el-col-push-8{position:relative;left:33.33333%}.custom-theme .el-col-9{width:37.5%}.custom-theme .el-col-offset-9{margin-left:37.5%}.custom-theme .el-col-pull-9{position:relative;right:37.5%}.custom-theme .el-col-push-9{position:relative;left:37.5%}.custom-theme .el-col-10{width:41.66667%}.custom-theme .el-col-offset-10{margin-left:41.66667%}.custom-theme .el-col-pull-10{position:relative;right:41.66667%}.custom-theme .el-col-push-10{position:relative;left:41.66667%}.custom-theme .el-col-11{width:45.83333%}.custom-theme .el-col-offset-11{margin-left:45.83333%}.custom-theme .el-col-pull-11{position:relative;right:45.83333%}.custom-theme .el-col-push-11{position:relative;left:45.83333%}.custom-theme .el-col-12{width:50%}.custom-theme .el-col-offset-12{margin-left:50%}.custom-theme .el-col-pull-12{position:relative;right:50%}.custom-theme .el-col-push-12{position:relative;left:50%}.custom-theme .el-col-13{width:54.16667%}.custom-theme .el-col-offset-13{margin-left:54.16667%}.custom-theme .el-col-pull-13{position:relative;right:54.16667%}.custom-theme .el-col-push-13{position:relative;left:54.16667%}.custom-theme .el-col-14{width:58.33333%}.custom-theme .el-col-offset-14{margin-left:58.33333%}.custom-theme .el-col-pull-14{position:relative;right:58.33333%}.custom-theme .el-col-push-14{position:relative;left:58.33333%}.custom-theme .el-col-15{width:62.5%}.custom-theme .el-col-offset-15{margin-left:62.5%}.custom-theme .el-col-pull-15{position:relative;right:62.5%}.custom-theme .el-col-push-15{position:relative;left:62.5%}.custom-theme .el-col-16{width:66.66667%}.custom-theme .el-col-offset-16{margin-left:66.66667%}.custom-theme .el-col-pull-16{position:relative;right:66.66667%}.custom-theme .el-col-push-16{position:relative;left:66.66667%}.custom-theme .el-col-17{width:70.83333%}.custom-theme .el-col-offset-17{margin-left:70.83333%}.custom-theme .el-col-pull-17{position:relative;right:70.83333%}.custom-theme .el-col-push-17{position:relative;left:70.83333%}.custom-theme .el-col-18{width:75%}.custom-theme .el-col-offset-18{margin-left:75%}.custom-theme .el-col-pull-18{position:relative;right:75%}.custom-theme .el-col-push-18{position:relative;left:75%}.custom-theme .el-col-19{width:79.16667%}.custom-theme .el-col-offset-19{margin-left:79.16667%}.custom-theme .el-col-pull-19{position:relative;right:79.16667%}.custom-theme .el-col-push-19{position:relative;left:79.16667%}.custom-theme .el-col-20{width:83.33333%}.custom-theme .el-col-offset-20{margin-left:83.33333%}.custom-theme .el-col-pull-20{position:relative;right:83.33333%}.custom-theme .el-col-push-20{position:relative;left:83.33333%}.custom-theme .el-col-21{width:87.5%}.custom-theme .el-col-offset-21{margin-left:87.5%}.custom-theme .el-col-pull-21{position:relative;right:87.5%}.custom-theme .el-col-push-21{position:relative;left:87.5%}.custom-theme .el-col-22{width:91.66667%}.custom-theme .el-col-offset-22{margin-left:91.66667%}.custom-theme .el-col-pull-22{position:relative;right:91.66667%}.custom-theme .el-col-push-22{position:relative;left:91.66667%}.custom-theme .el-col-23{width:95.83333%}.custom-theme .el-col-offset-23{margin-left:95.83333%}.custom-theme .el-col-pull-23{position:relative;right:95.83333%}.custom-theme .el-col-push-23{position:relative;left:95.83333%}.custom-theme .el-col-24{width:100%}.custom-theme .el-col-offset-24{margin-left:100%}.custom-theme .el-col-pull-24{position:relative;right:100%}.custom-theme .el-col-push-24{position:relative;left:100%}@media only screen and (max-width:768px){.custom-theme .el-col-xs-0{display:none}.custom-theme .el-col-xs-1{width:4.16667%}.custom-theme .el-col-xs-offset-1{margin-left:4.16667%}.custom-theme .el-col-xs-pull-1{position:relative;right:4.16667%}.custom-theme .el-col-xs-push-1{position:relative;left:4.16667%}.custom-theme .el-col-xs-2{width:8.33333%}.custom-theme .el-col-xs-offset-2{margin-left:8.33333%}.custom-theme .el-col-xs-pull-2{position:relative;right:8.33333%}.custom-theme .el-col-xs-push-2{position:relative;left:8.33333%}.custom-theme .el-col-xs-3{width:12.5%}.custom-theme .el-col-xs-offset-3{margin-left:12.5%}.custom-theme .el-col-xs-pull-3{position:relative;right:12.5%}.custom-theme .el-col-xs-push-3{position:relative;left:12.5%}.custom-theme .el-col-xs-4{width:16.66667%}.custom-theme .el-col-xs-offset-4{margin-left:16.66667%}.custom-theme .el-col-xs-pull-4{position:relative;right:16.66667%}.custom-theme .el-col-xs-push-4{position:relative;left:16.66667%}.custom-theme .el-col-xs-5{width:20.83333%}.custom-theme .el-col-xs-offset-5{margin-left:20.83333%}.custom-theme .el-col-xs-pull-5{position:relative;right:20.83333%}.custom-theme .el-col-xs-push-5{position:relative;left:20.83333%}.custom-theme .el-col-xs-6{width:25%}.custom-theme .el-col-xs-offset-6{margin-left:25%}.custom-theme .el-col-xs-pull-6{position:relative;right:25%}.custom-theme .el-col-xs-push-6{position:relative;left:25%}.custom-theme .el-col-xs-7{width:29.16667%}.custom-theme .el-col-xs-offset-7{margin-left:29.16667%}.custom-theme .el-col-xs-pull-7{position:relative;right:29.16667%}.custom-theme .el-col-xs-push-7{position:relative;left:29.16667%}.custom-theme .el-col-xs-8{width:33.33333%}.custom-theme .el-col-xs-offset-8{margin-left:33.33333%}.custom-theme .el-col-xs-pull-8{position:relative;right:33.33333%}.custom-theme .el-col-xs-push-8{position:relative;left:33.33333%}.custom-theme .el-col-xs-9{width:37.5%}.custom-theme .el-col-xs-offset-9{margin-left:37.5%}.custom-theme .el-col-xs-pull-9{position:relative;right:37.5%}.custom-theme .el-col-xs-push-9{position:relative;left:37.5%}.custom-theme .el-col-xs-10{width:41.66667%}.custom-theme .el-col-xs-offset-10{margin-left:41.66667%}.custom-theme .el-col-xs-pull-10{position:relative;right:41.66667%}.custom-theme .el-col-xs-push-10{position:relative;left:41.66667%}.custom-theme .el-col-xs-11{width:45.83333%}.custom-theme .el-col-xs-offset-11{margin-left:45.83333%}.custom-theme .el-col-xs-pull-11{position:relative;right:45.83333%}.custom-theme .el-col-xs-push-11{position:relative;left:45.83333%}.custom-theme .el-col-xs-12{width:50%}.custom-theme .el-col-xs-offset-12{margin-left:50%}.custom-theme .el-col-xs-pull-12{position:relative;right:50%}.custom-theme .el-col-xs-push-12{position:relative;left:50%}.custom-theme .el-col-xs-13{width:54.16667%}.custom-theme .el-col-xs-offset-13{margin-left:54.16667%}.custom-theme .el-col-xs-pull-13{position:relative;right:54.16667%}.custom-theme .el-col-xs-push-13{position:relative;left:54.16667%}.custom-theme .el-col-xs-14{width:58.33333%}.custom-theme .el-col-xs-offset-14{margin-left:58.33333%}.custom-theme .el-col-xs-pull-14{position:relative;right:58.33333%}.custom-theme .el-col-xs-push-14{position:relative;left:58.33333%}.custom-theme .el-col-xs-15{width:62.5%}.custom-theme .el-col-xs-offset-15{margin-left:62.5%}.custom-theme .el-col-xs-pull-15{position:relative;right:62.5%}.custom-theme .el-col-xs-push-15{position:relative;left:62.5%}.custom-theme .el-col-xs-16{width:66.66667%}.custom-theme .el-col-xs-offset-16{margin-left:66.66667%}.custom-theme .el-col-xs-pull-16{position:relative;right:66.66667%}.custom-theme .el-col-xs-push-16{position:relative;left:66.66667%}.custom-theme .el-col-xs-17{width:70.83333%}.custom-theme .el-col-xs-offset-17{margin-left:70.83333%}.custom-theme .el-col-xs-pull-17{position:relative;right:70.83333%}.custom-theme .el-col-xs-push-17{position:relative;left:70.83333%}.custom-theme .el-col-xs-18{width:75%}.custom-theme .el-col-xs-offset-18{margin-left:75%}.custom-theme .el-col-xs-pull-18{position:relative;right:75%}.custom-theme .el-col-xs-push-18{position:relative;left:75%}.custom-theme .el-col-xs-19{width:79.16667%}.custom-theme .el-col-xs-offset-19{margin-left:79.16667%}.custom-theme .el-col-xs-pull-19{position:relative;right:79.16667%}.custom-theme .el-col-xs-push-19{position:relative;left:79.16667%}.custom-theme .el-col-xs-20{width:83.33333%}.custom-theme .el-col-xs-offset-20{margin-left:83.33333%}.custom-theme .el-col-xs-pull-20{position:relative;right:83.33333%}.custom-theme .el-col-xs-push-20{position:relative;left:83.33333%}.custom-theme .el-col-xs-21{width:87.5%}.custom-theme .el-col-xs-offset-21{margin-left:87.5%}.custom-theme .el-col-xs-pull-21{position:relative;right:87.5%}.custom-theme .el-col-xs-push-21{position:relative;left:87.5%}.custom-theme .el-col-xs-22{width:91.66667%}.custom-theme .el-col-xs-offset-22{margin-left:91.66667%}.custom-theme .el-col-xs-pull-22{position:relative;right:91.66667%}.custom-theme .el-col-xs-push-22{position:relative;left:91.66667%}.custom-theme .el-col-xs-23{width:95.83333%}.custom-theme .el-col-xs-offset-23{margin-left:95.83333%}.custom-theme .el-col-xs-pull-23{position:relative;right:95.83333%}.custom-theme .el-col-xs-push-23{position:relative;left:95.83333%}.custom-theme .el-col-xs-24{width:100%}.custom-theme .el-col-xs-offset-24{margin-left:100%}.custom-theme .el-col-xs-pull-24{position:relative;right:100%}.custom-theme .el-col-xs-push-24{position:relative;left:100%}}@media only screen and (min-width:768px){.custom-theme .el-col-sm-0{display:none}.custom-theme .el-col-sm-1{width:4.16667%}.custom-theme .el-col-sm-offset-1{margin-left:4.16667%}.custom-theme .el-col-sm-pull-1{position:relative;right:4.16667%}.custom-theme .el-col-sm-push-1{position:relative;left:4.16667%}.custom-theme .el-col-sm-2{width:8.33333%}.custom-theme .el-col-sm-offset-2{margin-left:8.33333%}.custom-theme .el-col-sm-pull-2{position:relative;right:8.33333%}.custom-theme .el-col-sm-push-2{position:relative;left:8.33333%}.custom-theme .el-col-sm-3{width:12.5%}.custom-theme .el-col-sm-offset-3{margin-left:12.5%}.custom-theme .el-col-sm-pull-3{position:relative;right:12.5%}.custom-theme .el-col-sm-push-3{position:relative;left:12.5%}.custom-theme .el-col-sm-4{width:16.66667%}.custom-theme .el-col-sm-offset-4{margin-left:16.66667%}.custom-theme .el-col-sm-pull-4{position:relative;right:16.66667%}.custom-theme .el-col-sm-push-4{position:relative;left:16.66667%}.custom-theme .el-col-sm-5{width:20.83333%}.custom-theme .el-col-sm-offset-5{margin-left:20.83333%}.custom-theme .el-col-sm-pull-5{position:relative;right:20.83333%}.custom-theme .el-col-sm-push-5{position:relative;left:20.83333%}.custom-theme .el-col-sm-6{width:25%}.custom-theme .el-col-sm-offset-6{margin-left:25%}.custom-theme .el-col-sm-pull-6{position:relative;right:25%}.custom-theme .el-col-sm-push-6{position:relative;left:25%}.custom-theme .el-col-sm-7{width:29.16667%}.custom-theme .el-col-sm-offset-7{margin-left:29.16667%}.custom-theme .el-col-sm-pull-7{position:relative;right:29.16667%}.custom-theme .el-col-sm-push-7{position:relative;left:29.16667%}.custom-theme .el-col-sm-8{width:33.33333%}.custom-theme .el-col-sm-offset-8{margin-left:33.33333%}.custom-theme .el-col-sm-pull-8{position:relative;right:33.33333%}.custom-theme .el-col-sm-push-8{position:relative;left:33.33333%}.custom-theme .el-col-sm-9{width:37.5%}.custom-theme .el-col-sm-offset-9{margin-left:37.5%}.custom-theme .el-col-sm-pull-9{position:relative;right:37.5%}.custom-theme .el-col-sm-push-9{position:relative;left:37.5%}.custom-theme .el-col-sm-10{width:41.66667%}.custom-theme .el-col-sm-offset-10{margin-left:41.66667%}.custom-theme .el-col-sm-pull-10{position:relative;right:41.66667%}.custom-theme .el-col-sm-push-10{position:relative;left:41.66667%}.custom-theme .el-col-sm-11{width:45.83333%}.custom-theme .el-col-sm-offset-11{margin-left:45.83333%}.custom-theme .el-col-sm-pull-11{position:relative;right:45.83333%}.custom-theme .el-col-sm-push-11{position:relative;left:45.83333%}.custom-theme .el-col-sm-12{width:50%}.custom-theme .el-col-sm-offset-12{margin-left:50%}.custom-theme .el-col-sm-pull-12{position:relative;right:50%}.custom-theme .el-col-sm-push-12{position:relative;left:50%}.custom-theme .el-col-sm-13{width:54.16667%}.custom-theme .el-col-sm-offset-13{margin-left:54.16667%}.custom-theme .el-col-sm-pull-13{position:relative;right:54.16667%}.custom-theme .el-col-sm-push-13{position:relative;left:54.16667%}.custom-theme .el-col-sm-14{width:58.33333%}.custom-theme .el-col-sm-offset-14{margin-left:58.33333%}.custom-theme .el-col-sm-pull-14{position:relative;right:58.33333%}.custom-theme .el-col-sm-push-14{position:relative;left:58.33333%}.custom-theme .el-col-sm-15{width:62.5%}.custom-theme .el-col-sm-offset-15{margin-left:62.5%}.custom-theme .el-col-sm-pull-15{position:relative;right:62.5%}.custom-theme .el-col-sm-push-15{position:relative;left:62.5%}.custom-theme .el-col-sm-16{width:66.66667%}.custom-theme .el-col-sm-offset-16{margin-left:66.66667%}.custom-theme .el-col-sm-pull-16{position:relative;right:66.66667%}.custom-theme .el-col-sm-push-16{position:relative;left:66.66667%}.custom-theme .el-col-sm-17{width:70.83333%}.custom-theme .el-col-sm-offset-17{margin-left:70.83333%}.custom-theme .el-col-sm-pull-17{position:relative;right:70.83333%}.custom-theme .el-col-sm-push-17{position:relative;left:70.83333%}.custom-theme .el-col-sm-18{width:75%}.custom-theme .el-col-sm-offset-18{margin-left:75%}.custom-theme .el-col-sm-pull-18{position:relative;right:75%}.custom-theme .el-col-sm-push-18{position:relative;left:75%}.custom-theme .el-col-sm-19{width:79.16667%}.custom-theme .el-col-sm-offset-19{margin-left:79.16667%}.custom-theme .el-col-sm-pull-19{position:relative;right:79.16667%}.custom-theme .el-col-sm-push-19{position:relative;left:79.16667%}.custom-theme .el-col-sm-20{width:83.33333%}.custom-theme .el-col-sm-offset-20{margin-left:83.33333%}.custom-theme .el-col-sm-pull-20{position:relative;right:83.33333%}.custom-theme .el-col-sm-push-20{position:relative;left:83.33333%}.custom-theme .el-col-sm-21{width:87.5%}.custom-theme .el-col-sm-offset-21{margin-left:87.5%}.custom-theme .el-col-sm-pull-21{position:relative;right:87.5%}.custom-theme .el-col-sm-push-21{position:relative;left:87.5%}.custom-theme .el-col-sm-22{width:91.66667%}.custom-theme .el-col-sm-offset-22{margin-left:91.66667%}.custom-theme .el-col-sm-pull-22{position:relative;right:91.66667%}.custom-theme .el-col-sm-push-22{position:relative;left:91.66667%}.custom-theme .el-col-sm-23{width:95.83333%}.custom-theme .el-col-sm-offset-23{margin-left:95.83333%}.custom-theme .el-col-sm-pull-23{position:relative;right:95.83333%}.custom-theme .el-col-sm-push-23{position:relative;left:95.83333%}.custom-theme .el-col-sm-24{width:100%}.custom-theme .el-col-sm-offset-24{margin-left:100%}.custom-theme .el-col-sm-pull-24{position:relative;right:100%}.custom-theme .el-col-sm-push-24{position:relative;left:100%}}@media only screen and (min-width:992px){.custom-theme .el-col-md-0{display:none}.custom-theme .el-col-md-1{width:4.16667%}.custom-theme .el-col-md-offset-1{margin-left:4.16667%}.custom-theme .el-col-md-pull-1{position:relative;right:4.16667%}.custom-theme .el-col-md-push-1{position:relative;left:4.16667%}.custom-theme .el-col-md-2{width:8.33333%}.custom-theme .el-col-md-offset-2{margin-left:8.33333%}.custom-theme .el-col-md-pull-2{position:relative;right:8.33333%}.custom-theme .el-col-md-push-2{position:relative;left:8.33333%}.custom-theme .el-col-md-3{width:12.5%}.custom-theme .el-col-md-offset-3{margin-left:12.5%}.custom-theme .el-col-md-pull-3{position:relative;right:12.5%}.custom-theme .el-col-md-push-3{position:relative;left:12.5%}.custom-theme .el-col-md-4{width:16.66667%}.custom-theme .el-col-md-offset-4{margin-left:16.66667%}.custom-theme .el-col-md-pull-4{position:relative;right:16.66667%}.custom-theme .el-col-md-push-4{position:relative;left:16.66667%}.custom-theme .el-col-md-5{width:20.83333%}.custom-theme .el-col-md-offset-5{margin-left:20.83333%}.custom-theme .el-col-md-pull-5{position:relative;right:20.83333%}.custom-theme .el-col-md-push-5{position:relative;left:20.83333%}.custom-theme .el-col-md-6{width:25%}.custom-theme .el-col-md-offset-6{margin-left:25%}.custom-theme .el-col-md-pull-6{position:relative;right:25%}.custom-theme .el-col-md-push-6{position:relative;left:25%}.custom-theme .el-col-md-7{width:29.16667%}.custom-theme .el-col-md-offset-7{margin-left:29.16667%}.custom-theme .el-col-md-pull-7{position:relative;right:29.16667%}.custom-theme .el-col-md-push-7{position:relative;left:29.16667%}.custom-theme .el-col-md-8{width:33.33333%}.custom-theme .el-col-md-offset-8{margin-left:33.33333%}.custom-theme .el-col-md-pull-8{position:relative;right:33.33333%}.custom-theme .el-col-md-push-8{position:relative;left:33.33333%}.custom-theme .el-col-md-9{width:37.5%}.custom-theme .el-col-md-offset-9{margin-left:37.5%}.custom-theme .el-col-md-pull-9{position:relative;right:37.5%}.custom-theme .el-col-md-push-9{position:relative;left:37.5%}.custom-theme .el-col-md-10{width:41.66667%}.custom-theme .el-col-md-offset-10{margin-left:41.66667%}.custom-theme .el-col-md-pull-10{position:relative;right:41.66667%}.custom-theme .el-col-md-push-10{position:relative;left:41.66667%}.custom-theme .el-col-md-11{width:45.83333%}.custom-theme .el-col-md-offset-11{margin-left:45.83333%}.custom-theme .el-col-md-pull-11{position:relative;right:45.83333%}.custom-theme .el-col-md-push-11{position:relative;left:45.83333%}.custom-theme .el-col-md-12{width:50%}.custom-theme .el-col-md-offset-12{margin-left:50%}.custom-theme .el-col-md-pull-12{position:relative;right:50%}.custom-theme .el-col-md-push-12{position:relative;left:50%}.custom-theme .el-col-md-13{width:54.16667%}.custom-theme .el-col-md-offset-13{margin-left:54.16667%}.custom-theme .el-col-md-pull-13{position:relative;right:54.16667%}.custom-theme .el-col-md-push-13{position:relative;left:54.16667%}.custom-theme .el-col-md-14{width:58.33333%}.custom-theme .el-col-md-offset-14{margin-left:58.33333%}.custom-theme .el-col-md-pull-14{position:relative;right:58.33333%}.custom-theme .el-col-md-push-14{position:relative;left:58.33333%}.custom-theme .el-col-md-15{width:62.5%}.custom-theme .el-col-md-offset-15{margin-left:62.5%}.custom-theme .el-col-md-pull-15{position:relative;right:62.5%}.custom-theme .el-col-md-push-15{position:relative;left:62.5%}.custom-theme .el-col-md-16{width:66.66667%}.custom-theme .el-col-md-offset-16{margin-left:66.66667%}.custom-theme .el-col-md-pull-16{position:relative;right:66.66667%}.custom-theme .el-col-md-push-16{position:relative;left:66.66667%}.custom-theme .el-col-md-17{width:70.83333%}.custom-theme .el-col-md-offset-17{margin-left:70.83333%}.custom-theme .el-col-md-pull-17{position:relative;right:70.83333%}.custom-theme .el-col-md-push-17{position:relative;left:70.83333%}.custom-theme .el-col-md-18{width:75%}.custom-theme .el-col-md-offset-18{margin-left:75%}.custom-theme .el-col-md-pull-18{position:relative;right:75%}.custom-theme .el-col-md-push-18{position:relative;left:75%}.custom-theme .el-col-md-19{width:79.16667%}.custom-theme .el-col-md-offset-19{margin-left:79.16667%}.custom-theme .el-col-md-pull-19{position:relative;right:79.16667%}.custom-theme .el-col-md-push-19{position:relative;left:79.16667%}.custom-theme .el-col-md-20{width:83.33333%}.custom-theme .el-col-md-offset-20{margin-left:83.33333%}.custom-theme .el-col-md-pull-20{position:relative;right:83.33333%}.custom-theme .el-col-md-push-20{position:relative;left:83.33333%}.custom-theme .el-col-md-21{width:87.5%}.custom-theme .el-col-md-offset-21{margin-left:87.5%}.custom-theme .el-col-md-pull-21{position:relative;right:87.5%}.custom-theme .el-col-md-push-21{position:relative;left:87.5%}.custom-theme .el-col-md-22{width:91.66667%}.custom-theme .el-col-md-offset-22{margin-left:91.66667%}.custom-theme .el-col-md-pull-22{position:relative;right:91.66667%}.custom-theme .el-col-md-push-22{position:relative;left:91.66667%}.custom-theme .el-col-md-23{width:95.83333%}.custom-theme .el-col-md-offset-23{margin-left:95.83333%}.custom-theme .el-col-md-pull-23{position:relative;right:95.83333%}.custom-theme .el-col-md-push-23{position:relative;left:95.83333%}.custom-theme .el-col-md-24{width:100%}.custom-theme .el-col-md-offset-24{margin-left:100%}.custom-theme .el-col-md-pull-24{position:relative;right:100%}.custom-theme .el-col-md-push-24{position:relative;left:100%}}@media only screen and (min-width:1200px){.custom-theme .el-col-lg-0{display:none}.custom-theme .el-col-lg-1{width:4.16667%}.custom-theme .el-col-lg-offset-1{margin-left:4.16667%}.custom-theme .el-col-lg-pull-1{position:relative;right:4.16667%}.custom-theme .el-col-lg-push-1{position:relative;left:4.16667%}.custom-theme .el-col-lg-2{width:8.33333%}.custom-theme .el-col-lg-offset-2{margin-left:8.33333%}.custom-theme .el-col-lg-pull-2{position:relative;right:8.33333%}.custom-theme .el-col-lg-push-2{position:relative;left:8.33333%}.custom-theme .el-col-lg-3{width:12.5%}.custom-theme .el-col-lg-offset-3{margin-left:12.5%}.custom-theme .el-col-lg-pull-3{position:relative;right:12.5%}.custom-theme .el-col-lg-push-3{position:relative;left:12.5%}.custom-theme .el-col-lg-4{width:16.66667%}.custom-theme .el-col-lg-offset-4{margin-left:16.66667%}.custom-theme .el-col-lg-pull-4{position:relative;right:16.66667%}.custom-theme .el-col-lg-push-4{position:relative;left:16.66667%}.custom-theme .el-col-lg-5{width:20.83333%}.custom-theme .el-col-lg-offset-5{margin-left:20.83333%}.custom-theme .el-col-lg-pull-5{position:relative;right:20.83333%}.custom-theme .el-col-lg-push-5{position:relative;left:20.83333%}.custom-theme .el-col-lg-6{width:25%}.custom-theme .el-col-lg-offset-6{margin-left:25%}.custom-theme .el-col-lg-pull-6{position:relative;right:25%}.custom-theme .el-col-lg-push-6{position:relative;left:25%}.custom-theme .el-col-lg-7{width:29.16667%}.custom-theme .el-col-lg-offset-7{margin-left:29.16667%}.custom-theme .el-col-lg-pull-7{position:relative;right:29.16667%}.custom-theme .el-col-lg-push-7{position:relative;left:29.16667%}.custom-theme .el-col-lg-8{width:33.33333%}.custom-theme .el-col-lg-offset-8{margin-left:33.33333%}.custom-theme .el-col-lg-pull-8{position:relative;right:33.33333%}.custom-theme .el-col-lg-push-8{position:relative;left:33.33333%}.custom-theme .el-col-lg-9{width:37.5%}.custom-theme .el-col-lg-offset-9{margin-left:37.5%}.custom-theme .el-col-lg-pull-9{position:relative;right:37.5%}.custom-theme .el-col-lg-push-9{position:relative;left:37.5%}.custom-theme .el-col-lg-10{width:41.66667%}.custom-theme .el-col-lg-offset-10{margin-left:41.66667%}.custom-theme .el-col-lg-pull-10{position:relative;right:41.66667%}.custom-theme .el-col-lg-push-10{position:relative;left:41.66667%}.custom-theme .el-col-lg-11{width:45.83333%}.custom-theme .el-col-lg-offset-11{margin-left:45.83333%}.custom-theme .el-col-lg-pull-11{position:relative;right:45.83333%}.custom-theme .el-col-lg-push-11{position:relative;left:45.83333%}.custom-theme .el-col-lg-12{width:50%}.custom-theme .el-col-lg-offset-12{margin-left:50%}.custom-theme .el-col-lg-pull-12{position:relative;right:50%}.custom-theme .el-col-lg-push-12{position:relative;left:50%}.custom-theme .el-col-lg-13{width:54.16667%}.custom-theme .el-col-lg-offset-13{margin-left:54.16667%}.custom-theme .el-col-lg-pull-13{position:relative;right:54.16667%}.custom-theme .el-col-lg-push-13{position:relative;left:54.16667%}.custom-theme .el-col-lg-14{width:58.33333%}.custom-theme .el-col-lg-offset-14{margin-left:58.33333%}.custom-theme .el-col-lg-pull-14{position:relative;right:58.33333%}.custom-theme .el-col-lg-push-14{position:relative;left:58.33333%}.custom-theme .el-col-lg-15{width:62.5%}.custom-theme .el-col-lg-offset-15{margin-left:62.5%}.custom-theme .el-col-lg-pull-15{position:relative;right:62.5%}.custom-theme .el-col-lg-push-15{position:relative;left:62.5%}.custom-theme .el-col-lg-16{width:66.66667%}.custom-theme .el-col-lg-offset-16{margin-left:66.66667%}.custom-theme .el-col-lg-pull-16{position:relative;right:66.66667%}.custom-theme .el-col-lg-push-16{position:relative;left:66.66667%}.custom-theme .el-col-lg-17{width:70.83333%}.custom-theme .el-col-lg-offset-17{margin-left:70.83333%}.custom-theme .el-col-lg-pull-17{position:relative;right:70.83333%}.custom-theme .el-col-lg-push-17{position:relative;left:70.83333%}.custom-theme .el-col-lg-18{width:75%}.custom-theme .el-col-lg-offset-18{margin-left:75%}.custom-theme .el-col-lg-pull-18{position:relative;right:75%}.custom-theme .el-col-lg-push-18{position:relative;left:75%}.custom-theme .el-col-lg-19{width:79.16667%}.custom-theme .el-col-lg-offset-19{margin-left:79.16667%}.custom-theme .el-col-lg-pull-19{position:relative;right:79.16667%}.custom-theme .el-col-lg-push-19{position:relative;left:79.16667%}.custom-theme .el-col-lg-20{width:83.33333%}.custom-theme .el-col-lg-offset-20{margin-left:83.33333%}.custom-theme .el-col-lg-pull-20{position:relative;right:83.33333%}.custom-theme .el-col-lg-push-20{position:relative;left:83.33333%}.custom-theme .el-col-lg-21{width:87.5%}.custom-theme .el-col-lg-offset-21{margin-left:87.5%}.custom-theme .el-col-lg-pull-21{position:relative;right:87.5%}.custom-theme .el-col-lg-push-21{position:relative;left:87.5%}.custom-theme .el-col-lg-22{width:91.66667%}.custom-theme .el-col-lg-offset-22{margin-left:91.66667%}.custom-theme .el-col-lg-pull-22{position:relative;right:91.66667%}.custom-theme .el-col-lg-push-22{position:relative;left:91.66667%}.custom-theme .el-col-lg-23{width:95.83333%}.custom-theme .el-col-lg-offset-23{margin-left:95.83333%}.custom-theme .el-col-lg-pull-23{position:relative;right:95.83333%}.custom-theme .el-col-lg-push-23{position:relative;left:95.83333%}.custom-theme .el-col-lg-24{width:100%}.custom-theme .el-col-lg-offset-24{margin-left:100%}.custom-theme .el-col-lg-pull-24{position:relative;right:100%}.custom-theme .el-col-lg-push-24{position:relative;left:100%}}@media only screen and (min-width:1920px){.custom-theme .el-col-xl-0{display:none}.custom-theme .el-col-xl-1{width:4.16667%}.custom-theme .el-col-xl-offset-1{margin-left:4.16667%}.custom-theme .el-col-xl-pull-1{position:relative;right:4.16667%}.custom-theme .el-col-xl-push-1{position:relative;left:4.16667%}.custom-theme .el-col-xl-2{width:8.33333%}.custom-theme .el-col-xl-offset-2{margin-left:8.33333%}.custom-theme .el-col-xl-pull-2{position:relative;right:8.33333%}.custom-theme .el-col-xl-push-2{position:relative;left:8.33333%}.custom-theme .el-col-xl-3{width:12.5%}.custom-theme .el-col-xl-offset-3{margin-left:12.5%}.custom-theme .el-col-xl-pull-3{position:relative;right:12.5%}.custom-theme .el-col-xl-push-3{position:relative;left:12.5%}.custom-theme .el-col-xl-4{width:16.66667%}.custom-theme .el-col-xl-offset-4{margin-left:16.66667%}.custom-theme .el-col-xl-pull-4{position:relative;right:16.66667%}.custom-theme .el-col-xl-push-4{position:relative;left:16.66667%}.custom-theme .el-col-xl-5{width:20.83333%}.custom-theme .el-col-xl-offset-5{margin-left:20.83333%}.custom-theme .el-col-xl-pull-5{position:relative;right:20.83333%}.custom-theme .el-col-xl-push-5{position:relative;left:20.83333%}.custom-theme .el-col-xl-6{width:25%}.custom-theme .el-col-xl-offset-6{margin-left:25%}.custom-theme .el-col-xl-pull-6{position:relative;right:25%}.custom-theme .el-col-xl-push-6{position:relative;left:25%}.custom-theme .el-col-xl-7{width:29.16667%}.custom-theme .el-col-xl-offset-7{margin-left:29.16667%}.custom-theme .el-col-xl-pull-7{position:relative;right:29.16667%}.custom-theme .el-col-xl-push-7{position:relative;left:29.16667%}.custom-theme .el-col-xl-8{width:33.33333%}.custom-theme .el-col-xl-offset-8{margin-left:33.33333%}.custom-theme .el-col-xl-pull-8{position:relative;right:33.33333%}.custom-theme .el-col-xl-push-8{position:relative;left:33.33333%}.custom-theme .el-col-xl-9{width:37.5%}.custom-theme .el-col-xl-offset-9{margin-left:37.5%}.custom-theme .el-col-xl-pull-9{position:relative;right:37.5%}.custom-theme .el-col-xl-push-9{position:relative;left:37.5%}.custom-theme .el-col-xl-10{width:41.66667%}.custom-theme .el-col-xl-offset-10{margin-left:41.66667%}.custom-theme .el-col-xl-pull-10{position:relative;right:41.66667%}.custom-theme .el-col-xl-push-10{position:relative;left:41.66667%}.custom-theme .el-col-xl-11{width:45.83333%}.custom-theme .el-col-xl-offset-11{margin-left:45.83333%}.custom-theme .el-col-xl-pull-11{position:relative;right:45.83333%}.custom-theme .el-col-xl-push-11{position:relative;left:45.83333%}.custom-theme .el-col-xl-12{width:50%}.custom-theme .el-col-xl-offset-12{margin-left:50%}.custom-theme .el-col-xl-pull-12{position:relative;right:50%}.custom-theme .el-col-xl-push-12{position:relative;left:50%}.custom-theme .el-col-xl-13{width:54.16667%}.custom-theme .el-col-xl-offset-13{margin-left:54.16667%}.custom-theme .el-col-xl-pull-13{position:relative;right:54.16667%}.custom-theme .el-col-xl-push-13{position:relative;left:54.16667%}.custom-theme .el-col-xl-14{width:58.33333%}.custom-theme .el-col-xl-offset-14{margin-left:58.33333%}.custom-theme .el-col-xl-pull-14{position:relative;right:58.33333%}.custom-theme .el-col-xl-push-14{position:relative;left:58.33333%}.custom-theme .el-col-xl-15{width:62.5%}.custom-theme .el-col-xl-offset-15{margin-left:62.5%}.custom-theme .el-col-xl-pull-15{position:relative;right:62.5%}.custom-theme .el-col-xl-push-15{position:relative;left:62.5%}.custom-theme .el-col-xl-16{width:66.66667%}.custom-theme .el-col-xl-offset-16{margin-left:66.66667%}.custom-theme .el-col-xl-pull-16{position:relative;right:66.66667%}.custom-theme .el-col-xl-push-16{position:relative;left:66.66667%}.custom-theme .el-col-xl-17{width:70.83333%}.custom-theme .el-col-xl-offset-17{margin-left:70.83333%}.custom-theme .el-col-xl-pull-17{position:relative;right:70.83333%}.custom-theme .el-col-xl-push-17{position:relative;left:70.83333%}.custom-theme .el-col-xl-18{width:75%}.custom-theme .el-col-xl-offset-18{margin-left:75%}.custom-theme .el-col-xl-pull-18{position:relative;right:75%}.custom-theme .el-col-xl-push-18{position:relative;left:75%}.custom-theme .el-col-xl-19{width:79.16667%}.custom-theme .el-col-xl-offset-19{margin-left:79.16667%}.custom-theme .el-col-xl-pull-19{position:relative;right:79.16667%}.custom-theme .el-col-xl-push-19{position:relative;left:79.16667%}.custom-theme .el-col-xl-20{width:83.33333%}.custom-theme .el-col-xl-offset-20{margin-left:83.33333%}.custom-theme .el-col-xl-pull-20{position:relative;right:83.33333%}.custom-theme .el-col-xl-push-20{position:relative;left:83.33333%}.custom-theme .el-col-xl-21{width:87.5%}.custom-theme .el-col-xl-offset-21{margin-left:87.5%}.custom-theme .el-col-xl-pull-21{position:relative;right:87.5%}.custom-theme .el-col-xl-push-21{position:relative;left:87.5%}.custom-theme .el-col-xl-22{width:91.66667%}.custom-theme .el-col-xl-offset-22{margin-left:91.66667%}.custom-theme .el-col-xl-pull-22{position:relative;right:91.66667%}.custom-theme .el-col-xl-push-22{position:relative;left:91.66667%}.custom-theme .el-col-xl-23{width:95.83333%}.custom-theme .el-col-xl-offset-23{margin-left:95.83333%}.custom-theme .el-col-xl-pull-23{position:relative;right:95.83333%}.custom-theme .el-col-xl-push-23{position:relative;left:95.83333%}.custom-theme .el-col-xl-24{width:100%}.custom-theme .el-col-xl-offset-24{margin-left:100%}.custom-theme .el-col-xl-pull-24{position:relative;right:100%}.custom-theme .el-col-xl-push-24{position:relative;left:100%}}.custom-theme .el-progress{position:relative;line-height:1}.custom-theme .el-progress__text{font-size:14px;color:#5a5e66;display:inline-block;vertical-align:middle;margin-left:10px;line-height:1}.custom-theme .el-progress__text i{vertical-align:middle;display:block}.custom-theme .el-progress--circle{display:inline-block}.custom-theme .el-progress--circle .el-progress__text{position:absolute;top:50%;left:0;width:100%;text-align:center;margin:0;-webkit-transform:translate(0,-50%);transform:translate(0,-50%)}.custom-theme .el-progress--circle .el-progress__text i{vertical-align:middle;display:inline-block}.custom-theme .el-progress--without-text .el-progress__text{display:none}.custom-theme .el-progress--without-text .el-progress-bar{padding-right:0;margin-right:0;display:block}.custom-theme .el-progress--text-inside .el-progress-bar{padding-right:0;margin-right:0}.custom-theme .el-progress.is-success .el-progress-bar__inner{background-color:#409167}.custom-theme .el-progress.is-success .el-progress__text{color:#409167}.custom-theme .el-progress.is-exception .el-progress-bar__inner{background-color:#b3450e}.custom-theme .el-progress.is-exception .el-progress__text{color:#b3450e}.custom-theme .el-progress-bar{padding-right:50px;display:inline-block;vertical-align:middle;width:100%;margin-right:-55px;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-progress-bar__outer{height:6px;border-radius:100px;background-color:#e6ebf5;overflow:hidden;position:relative;vertical-align:middle}.custom-theme .el-progress-bar__inner{position:absolute;left:0;top:0;height:100%;background-color:#262729;text-align:right;border-radius:100px;line-height:1;white-space:nowrap}.custom-theme .el-progress-bar__inner::after{display:inline-block;content:\"\";height:100%;vertical-align:middle}.custom-theme .el-progress-bar__innerText{display:inline-block;vertical-align:middle;color:#fff;font-size:12px;margin:0 5px}@-webkit-keyframes progress{0%{background-position:0 0}100%{background-position:32px 0}}@keyframes progress{0%{background-position:0 0}100%{background-position:32px 0}}.custom-theme .el-upload{display:inline-block;text-align:center;cursor:pointer}.custom-theme .el-upload__input{display:none}.custom-theme .el-upload__tip{font-size:12px;color:#5a5e66;margin-top:7px}.custom-theme .el-upload iframe{position:absolute;z-index:-1;top:0;left:0;opacity:0}.custom-theme .el-upload--picture-card{background-color:#fbfdff;border:1px dashed #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;width:148px;height:148px;cursor:pointer;line-height:146px;vertical-align:top}.custom-theme .el-upload--picture-card i{font-size:28px;color:#8c939d}.custom-theme .el-upload--picture-card:hover{border-color:#262729;color:#262729}.custom-theme .el-upload-dragger{background-color:#fff;border:1px dashed #d9d9d9;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;width:360px;height:180px;text-align:center;cursor:pointer;position:relative;overflow:hidden}.custom-theme .el-upload-dragger .el-icon-upload{font-size:67px;color:#b4bccc;margin:40px 0 16px;line-height:50px}.custom-theme .el-upload-dragger+.el-upload__tip{text-align:center}.custom-theme .el-upload-dragger~.el-upload__files{border-top:1px solid #d8dce5;margin-top:7px;padding-top:5px}.custom-theme .el-upload-dragger .el-upload__text{color:#5a5e66;font-size:14px;text-align:center}.custom-theme .el-upload-dragger .el-upload__text em{color:#262729;font-style:normal}.custom-theme .el-upload-dragger:hover{border-color:#262729}.custom-theme .el-upload-dragger.is-dragover{background-color:rgba(32,159,255,.06);border:2px dashed #262729}.custom-theme .el-upload-list{margin:0;padding:0;list-style:none}.custom-theme .el-upload-list__item{-webkit-transition:all .5s cubic-bezier(.55,0,.1,1);transition:all .5s cubic-bezier(.55,0,.1,1);font-size:14px;color:#5a5e66;line-height:1.8;margin-top:5px;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;width:100%}.custom-theme .el-upload-list__item .el-progress{position:absolute;top:20px;width:100%}.custom-theme .el-upload-list__item .el-progress__text{position:absolute;right:0;top:-13px}.custom-theme .el-upload-list__item .el-progress-bar{margin-right:0;padding-right:0}.custom-theme .el-upload-list__item:first-child{margin-top:10px}.custom-theme .el-upload-list__item .el-icon-upload-success{color:#409167}.custom-theme .el-upload-list__item .el-icon-close{display:none;position:absolute;top:5px;right:5px;cursor:pointer;opacity:.75;color:#5a5e66}.custom-theme .el-upload-list__item .el-icon-close:hover{opacity:1}.custom-theme .el-upload-list__item .el-icon-close-tip{display:none;position:absolute;top:5px;right:0;cursor:pointer;opacity:1;color:#262729;-webkit-transform:translate(15%,0);transform:translate(15%,0)}.custom-theme .el-upload-list__item:hover{background-color:#f5f7fa}.custom-theme .el-upload-list__item:hover .el-icon-close{display:inline-block}.custom-theme .el-upload-list__item:hover .el-progress__text{display:none}.custom-theme .el-upload-list__item.is-success .el-upload-list__item-status-label{display:block}.custom-theme .el-upload-list__item.is-success .el-upload-list__item-name:focus,.custom-theme .el-upload-list__item.is-success .el-upload-list__item-name:hover{color:#262729;cursor:pointer}.custom-theme .el-upload-list__item.is-success:focus .el-icon-close-tip{display:inline-block}.custom-theme .el-upload-list__item.is-success:active,.custom-theme .el-upload-list__item.is-success:focus:not(.focusing){outline-width:0}.custom-theme .el-upload-list__item.is-success:active .el-icon-close-tip,.custom-theme .el-upload-list__item.is-success:focus:not(.focusing) .el-icon-close-tip{display:none}.custom-theme .el-upload-list__item.is-success:focus .el-upload-list__item-status-label,.custom-theme .el-upload-list__item.is-success:hover .el-upload-list__item-status-label{display:none}.custom-theme .el-upload-list.is-disabled .el-upload-list__item:hover .el-upload-list__item-status-label{display:block}.custom-theme .el-upload-list__item-name{color:#5a5e66;display:block;margin-right:40px;overflow:hidden;padding-left:4px;text-overflow:ellipsis;-webkit-transition:color .3s;transition:color .3s;white-space:nowrap}.custom-theme .el-upload-list__item-name [class^=el-icon]{height:100%;margin-right:7px;color:#878d99;line-height:inherit}.custom-theme .el-upload-list__item-status-label{position:absolute;right:5px;top:0;line-height:inherit;display:none}.custom-theme .el-upload-list__item-delete{position:absolute;right:10px;top:0;font-size:12px;color:#5a5e66;display:none}.custom-theme .el-upload-list__item-delete:hover{color:#262729}.custom-theme .el-upload-list--picture-card{margin:0;display:inline;vertical-align:top}.custom-theme .el-upload-list--picture-card .el-upload-list__item{overflow:hidden;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;width:148px;height:148px;margin:0 8px 8px 0;display:inline-block}.custom-theme .el-upload-list--picture-card .el-upload-list__item .el-icon-check,.custom-theme .el-upload-list--picture-card .el-upload-list__item .el-icon-circle-check{color:#fff}.custom-theme .el-upload-list--picture-card .el-upload-list__item .el-icon-close{display:none}.custom-theme .el-upload-list--picture-card .el-upload-list__item:hover .el-upload-list__item-status-label{display:none}.custom-theme .el-upload-list--picture-card .el-upload-list__item:hover .el-progress__text{display:block}.custom-theme .el-upload-list--picture-card .el-upload-list__item-name{display:none}.custom-theme .el-upload-list--picture-card .el-upload-list__item-thumbnail{width:100%;height:100%}.custom-theme .el-upload-list--picture-card .el-upload-list__item-status-label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 0 1pc 1px rgba(0,0,0,.2);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.custom-theme .el-upload-list--picture-card .el-upload-list__item-status-label i{font-size:12px;margin-top:11px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.custom-theme .el-upload-list--picture-card .el-upload-list__item-actions{position:absolute;width:100%;height:100%;left:0;top:0;cursor:default;text-align:center;color:#fff;opacity:0;font-size:20px;background-color:rgba(0,0,0,.5);-webkit-transition:opacity .3s;transition:opacity .3s}.custom-theme .el-upload-list--picture-card .el-upload-list__item-actions::after{display:inline-block;content:\"\";height:100%;vertical-align:middle}.custom-theme .el-upload-list--picture-card .el-upload-list__item-actions span{display:none;cursor:pointer}.custom-theme .el-upload-list--picture-card .el-upload-list__item-actions span+span{margin-left:15px}.custom-theme .el-upload-list--picture-card .el-upload-list__item-actions .el-upload-list__item-delete{position:static;font-size:inherit;color:inherit}.custom-theme .el-upload-list--picture-card .el-upload-list__item-actions:hover{opacity:1}.custom-theme .el-upload-list--picture-card .el-upload-list__item-actions:hover span{display:inline-block}.custom-theme .el-upload-list--picture-card .el-progress{top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);bottom:auto;width:126px}.custom-theme .el-upload-list--picture-card .el-progress .el-progress__text{top:50%}.custom-theme .el-upload-list--picture .el-upload-list__item{overflow:hidden;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;margin-top:10px;padding:10px 10px 10px 90px;height:92px}.custom-theme .el-upload-list--picture .el-upload-list__item .el-icon-check,.custom-theme .el-upload-list--picture .el-upload-list__item .el-icon-circle-check{color:#fff}.custom-theme .el-upload-list--picture .el-upload-list__item:hover .el-upload-list__item-status-label{background:0 0;-webkit-box-shadow:none;box-shadow:none;top:-2px;right:-12px}.custom-theme .el-upload-list--picture .el-upload-list__item:hover .el-progress__text{display:block}.custom-theme .el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name{line-height:70px;margin-top:0}.custom-theme .el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name i{display:none}.custom-theme .el-upload-list--picture .el-upload-list__item-thumbnail{vertical-align:middle;display:inline-block;width:70px;height:70px;float:left;position:relative;z-index:1;margin-left:-80px}.custom-theme .el-upload-list--picture .el-upload-list__item-name{display:block;margin-top:20px}.custom-theme .el-upload-list--picture .el-upload-list__item-name i{font-size:70px;line-height:1;position:absolute;left:9px;top:10px}.custom-theme .el-upload-list--picture .el-upload-list__item-status-label{position:absolute;right:-17px;top:-7px;width:46px;height:26px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 1px 1px #ccc;box-shadow:0 1px 1px #ccc}.custom-theme .el-upload-list--picture .el-upload-list__item-status-label i{font-size:12px;margin-top:12px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.custom-theme .el-upload-list--picture .el-progress{position:relative;top:-7px}.custom-theme .el-upload-cover{position:absolute;left:0;top:0;width:100%;height:100%;overflow:hidden;z-index:10;cursor:default}.custom-theme .el-upload-cover::after{display:inline-block;content:\"\";height:100%;vertical-align:middle}.custom-theme .el-upload-cover img{display:block;width:100%;height:100%}.custom-theme .el-upload-cover__label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 0 1pc 1px rgba(0,0,0,.2);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.custom-theme .el-upload-cover__label i{font-size:12px;margin-top:11px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);color:#fff}.custom-theme .el-upload-cover__progress{display:inline-block;vertical-align:middle;position:static;width:243px}.custom-theme .el-upload-cover__progress+.el-upload__inner{opacity:0}.custom-theme .el-upload-cover__content{position:absolute;top:0;left:0;width:100%;height:100%}.custom-theme .el-upload-cover__interact{position:absolute;bottom:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,.72);text-align:center}.custom-theme .el-upload-cover__interact .btn{display:inline-block;color:#fff;font-size:14px;cursor:pointer;vertical-align:middle;-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;margin-top:60px}.custom-theme .el-upload-cover__interact .btn i{margin-top:0}.custom-theme .el-upload-cover__interact .btn span{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.custom-theme .el-upload-cover__interact .btn:not(:first-child){margin-left:35px}.custom-theme .el-upload-cover__interact .btn:hover{-webkit-transform:translateY(-13px);transform:translateY(-13px)}.custom-theme .el-upload-cover__interact .btn:hover span{opacity:1}.custom-theme .el-upload-cover__interact .btn i{color:#fff;display:block;font-size:24px;line-height:inherit;margin:0 auto 5px}.custom-theme .el-upload-cover__title{position:absolute;bottom:0;left:0;background-color:#fff;height:36px;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:400;text-align:left;padding:0 10px;margin:0;line-height:36px;font-size:14px;color:#2d2f33}.custom-theme .el-upload-cover+.el-upload__inner{opacity:0;position:relative;z-index:1}.custom-theme .el-progress{position:relative;line-height:1}.custom-theme .el-progress__text{font-size:14px;color:#5a5e66;display:inline-block;vertical-align:middle;margin-left:10px;line-height:1}.custom-theme .el-progress__text i{vertical-align:middle;display:block}.custom-theme .el-progress--circle{display:inline-block}.custom-theme .el-progress--circle .el-progress__text{position:absolute;top:50%;left:0;width:100%;text-align:center;margin:0;-webkit-transform:translate(0,-50%);transform:translate(0,-50%)}.custom-theme .el-progress--circle .el-progress__text i{vertical-align:middle;display:inline-block}.custom-theme .el-progress--without-text .el-progress__text{display:none}.custom-theme .el-progress--without-text .el-progress-bar{padding-right:0;margin-right:0;display:block}.custom-theme .el-progress--text-inside .el-progress-bar{padding-right:0;margin-right:0}.custom-theme .el-progress.is-success .el-progress-bar__inner{background-color:#409167}.custom-theme .el-progress.is-success .el-progress__text{color:#409167}.custom-theme .el-progress.is-exception .el-progress-bar__inner{background-color:#b3450e}.custom-theme .el-progress.is-exception .el-progress__text{color:#b3450e}.custom-theme .el-progress-bar{padding-right:50px;display:inline-block;vertical-align:middle;width:100%;margin-right:-55px;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-progress-bar__outer{height:6px;border-radius:100px;background-color:#e6ebf5;overflow:hidden;position:relative;vertical-align:middle}.custom-theme .el-progress-bar__inner{position:absolute;left:0;top:0;height:100%;background-color:#262729;text-align:right;border-radius:100px;line-height:1;white-space:nowrap}.custom-theme .el-progress-bar__inner::after{display:inline-block;content:\"\";height:100%;vertical-align:middle}.custom-theme .el-progress-bar__innerText{display:inline-block;vertical-align:middle;color:#fff;font-size:12px;margin:0 5px}@keyframes progress{0%{background-position:0 0}100%{background-position:32px 0}}.custom-theme .el-time-spinner{width:100%;white-space:nowrap}.custom-theme .el-spinner{display:inline-block;vertical-align:middle}.custom-theme .el-spinner-inner{-webkit-animation:rotate 2s linear infinite;animation:rotate 2s linear infinite;width:50px;height:50px}.custom-theme .el-spinner-inner .path{stroke:#ececec;stroke-linecap:round;-webkit-animation:dash 1.5s ease-in-out infinite;animation:dash 1.5s ease-in-out infinite}@-webkit-keyframes rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}100%{stroke-dasharray:90,150;stroke-dashoffset:-124}}@keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}100%{stroke-dasharray:90,150;stroke-dashoffset:-124}}.custom-theme .el-message{min-width:380px;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;border-width:1px;border-style:solid;border-color:#e6ebf5;position:fixed;left:50%;top:20px;-webkit-transform:translateX(-50%);transform:translateX(-50%);background-color:#edf2fc;-webkit-transition:opacity .3s,-webkit-transform .4s;transition:opacity .3s,-webkit-transform .4s;transition:opacity .3s,transform .4s;transition:opacity .3s,transform .4s,-webkit-transform .4s;overflow:hidden;padding:15px 15px 15px 20px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.custom-theme .el-message.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.custom-theme .el-message p{margin:0}.custom-theme .el-message--info .el-message__content{color:#0a76a4}.custom-theme .el-message--success{background-color:#ecf4f0;border-color:#d9e9e1}.custom-theme .el-message--success .el-message__content{color:#409167}.custom-theme .el-message--warning{background-color:#f5f6e6;border-color:#ebedce}.custom-theme .el-message--warning .el-message__content{color:#9da408}.custom-theme .el-message--error{background-color:#f7ece7;border-color:#f0dacf}.custom-theme .el-message--error .el-message__content{color:#b3450e}.custom-theme .el-message__icon{margin-right:10px}.custom-theme .el-message__content{padding:0;font-size:14px;line-height:1}.custom-theme .el-message__content:focus{outline-width:0}.custom-theme .el-message__closeBtn{position:absolute;top:50%;right:15px;-webkit-transform:translateY(-50%);transform:translateY(-50%);cursor:pointer;color:#b4bccc;font-size:16px}.custom-theme .el-message__closeBtn:focus{outline-width:0}.custom-theme .el-message__closeBtn:hover{color:#878d99}.custom-theme .el-message .el-icon-success{color:#409167}.custom-theme .el-message .el-icon-error{color:#b3450e}.custom-theme .el-message .el-icon-info{color:#0a76a4}.custom-theme .el-message .el-icon-warning{color:#9da408}.custom-theme .el-message-fade-enter,.custom-theme .el-message-fade-leave-active{opacity:0;-webkit-transform:translate(-50%,-100%);transform:translate(-50%,-100%)}.custom-theme .el-badge{position:relative;vertical-align:middle;display:inline-block}.custom-theme .el-badge__content{background-color:#b3450e;border-radius:10px;color:#fff;display:inline-block;font-size:12px;height:18px;line-height:18px;padding:0 6px;text-align:center;white-space:nowrap;border:1px solid #fff}.custom-theme .el-badge__content.is-fixed{position:absolute;top:0;right:10px;-webkit-transform:translateY(-50%) translateX(100%);transform:translateY(-50%) translateX(100%)}.custom-theme .el-badge__content.is-fixed.is-dot{right:5px}.custom-theme .el-badge__content.is-dot{height:8px;width:8px;padding:0;right:0;border-radius:50%}.custom-theme .el-card{border-radius:4px;border:1px solid #e6ebf5;background-color:#fff;overflow:hidden;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);color:#2d2f33}.custom-theme .el-card__header{padding:18px 20px;border-bottom:1px solid #e6ebf5;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-card__body{padding:20px}.custom-theme .el-rate{height:20px;line-height:1}.custom-theme .el-rate:active,.custom-theme .el-rate:focus{outline-width:0}.custom-theme .el-rate__item{display:inline-block;position:relative;font-size:0;vertical-align:middle}.custom-theme .el-rate__icon{position:relative;display:inline-block;font-size:18px;margin-right:6px;color:#b4bccc;-webkit-transition:.3s;transition:.3s}.custom-theme .el-rate__icon.hover{-webkit-transform:scale(1.15);transform:scale(1.15)}.custom-theme .el-rate__icon .path2{position:absolute;left:0;top:0}.custom-theme .el-rate__decimal{position:absolute;top:0;left:0;display:inline-block;overflow:hidden}.custom-theme .el-rate__text{font-size:14px;vertical-align:middle}.custom-theme .el-steps{display:-webkit-box;display:-ms-flexbox;display:flex}.custom-theme .el-steps--simple{padding:13px 8%;border-radius:4px;background:#f5f7fa}.custom-theme .el-steps--horizontal{white-space:nowrap}.custom-theme .el-steps--vertical{height:100%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-flow:column;flex-flow:column}.custom-theme .el-step{position:relative;-ms-flex-negative:1;flex-shrink:1}.custom-theme .el-step:last-of-type .el-step__line{display:none}.custom-theme .el-step:last-of-type.is-flex{-ms-flex-preferred-size:auto!important;flex-basis:auto!important;-ms-flex-negative:0;flex-shrink:0;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0}.custom-theme .el-step:last-of-type .el-step__description,.custom-theme .el-step:last-of-type .el-step__main{padding-right:0}.custom-theme .el-step__head{position:relative;width:100%}.custom-theme .el-step__head.is-process{color:#2d2f33;border-color:#2d2f33}.custom-theme .el-step__head.is-wait{color:#b4bccc;border-color:#b4bccc}.custom-theme .el-step__head.is-success{color:#409167;border-color:#409167}.custom-theme .el-step__head.is-error{color:#b3450e;border-color:#b3450e}.custom-theme .el-step__head.is-finish{color:#262729;border-color:#262729}.custom-theme .el-step__icon{position:relative;z-index:1;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:24px;height:24px;font-size:14px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#fff;-webkit-transition:.15s ease-out;transition:.15s ease-out}.custom-theme .el-step__icon.is-text{border-radius:50%;border:2px solid;border-color:inherit}.custom-theme .el-step__icon.is-icon{width:40px}.custom-theme .el-step__icon-inner{display:inline-block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-align:center;font-weight:700;line-height:1;color:inherit}.custom-theme .el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:25px;font-weight:400}.custom-theme .el-step__icon-inner.is-status{-webkit-transform:translateY(1px);transform:translateY(1px)}.custom-theme .el-step__line{position:absolute;border-color:inherit;background-color:#b4bccc}.custom-theme .el-step__line-inner{display:block;border-width:1px;border-style:solid;border-color:inherit;-webkit-transition:.15s ease-out;transition:.15s ease-out;-webkit-box-sizing:border-box;box-sizing:border-box;width:0;height:0}.custom-theme .el-step__main{white-space:normal;text-align:left}.custom-theme .el-step__title{font-size:16px;line-height:38px}.custom-theme .el-step__title.is-process{font-weight:700;color:#2d2f33}.custom-theme .el-step__title.is-wait{color:#b4bccc}.custom-theme .el-step__title.is-success{color:#409167}.custom-theme .el-step__title.is-error{color:#b3450e}.custom-theme .el-step__title.is-finish{color:#262729}.custom-theme .el-step__description{padding-right:10%;margin-top:-5px;font-size:12px;line-height:20px;font-weight:400}.custom-theme .el-step__description.is-process{color:#2d2f33}.custom-theme .el-step__description.is-wait{color:#b4bccc}.custom-theme .el-step__description.is-success{color:#409167}.custom-theme .el-step__description.is-error{color:#b3450e}.custom-theme .el-step__description.is-finish{color:#262729}.custom-theme .el-step.is-horizontal{display:inline-block}.custom-theme .el-step.is-horizontal .el-step__line{height:2px;top:11px;left:0;right:0}.custom-theme .el-step.is-vertical{display:-webkit-box;display:-ms-flexbox;display:flex}.custom-theme .el-step.is-vertical .el-step__head{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;width:24px}.custom-theme .el-step.is-vertical .el-step__main{padding-left:10px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.custom-theme .el-step.is-vertical .el-step__title{line-height:24px;padding-bottom:8px}.custom-theme .el-step.is-vertical .el-step__line{width:2px;top:0;bottom:0;left:11px}.custom-theme .el-step.is-vertical .el-step__icon.is-icon{width:24px}.custom-theme .el-step.is-center .el-step__head{text-align:center}.custom-theme .el-step.is-center .el-step__main{text-align:center}.custom-theme .el-step.is-center .el-step__description{padding-left:20%;padding-right:20%}.custom-theme .el-step.is-center .el-step__line{left:50%;right:-50%}.custom-theme .el-step.is-simple{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.custom-theme .el-step.is-simple .el-step__head{width:auto;font-size:0;padding-right:10px}.custom-theme .el-step.is-simple .el-step__icon{background:0 0;width:16px;height:16px;font-size:12px}.custom-theme .el-step.is-simple .el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:18px}.custom-theme .el-step.is-simple .el-step__icon-inner.is-status{-webkit-transform:scale(.8) translateY(1px);transform:scale(.8) translateY(1px)}.custom-theme .el-step.is-simple .el-step__main{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.custom-theme .el-step.is-simple .el-step__title{font-size:16px;line-height:20px}.custom-theme .el-step.is-simple:not(:last-of-type) .el-step__title{max-width:50%;word-break:break-all}.custom-theme .el-step.is-simple .el-step__arrow{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.custom-theme .el-step.is-simple .el-step__arrow::after,.custom-theme .el-step.is-simple .el-step__arrow::before{content:'';display:inline-block;position:absolute;height:15px;width:1px;background:#b4bccc}.custom-theme .el-step.is-simple .el-step__arrow::before{-webkit-transform:rotate(-45deg) translateY(-4px);transform:rotate(-45deg) translateY(-4px);-webkit-transform-origin:0 0;transform-origin:0 0}.custom-theme .el-step.is-simple .el-step__arrow::after{-webkit-transform:rotate(45deg) translateY(4px);transform:rotate(45deg) translateY(4px);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}.custom-theme .el-step.is-simple:last-of-type .el-step__arrow{display:none}.custom-theme .el-carousel{overflow-x:hidden;position:relative}.custom-theme .el-carousel__container{position:relative;height:300px}.custom-theme .el-carousel__arrow{border:none;outline:0;padding:0;margin:0;height:36px;width:36px;cursor:pointer;-webkit-transition:.3s;transition:.3s;border-radius:50%;background-color:rgba(31,45,61,.11);color:#fff;position:absolute;top:50%;z-index:10;-webkit-transform:translateY(-50%);transform:translateY(-50%);text-align:center;font-size:12px}.custom-theme .el-carousel__arrow--left{left:16px}.custom-theme .el-carousel__arrow--right{right:16px}.custom-theme .el-carousel__arrow:hover{background-color:rgba(31,45,61,.23)}.custom-theme .el-carousel__arrow i{cursor:pointer}.custom-theme .el-carousel__indicators{position:absolute;list-style:none;bottom:0;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);margin:0;padding:0;z-index:2}.custom-theme .el-carousel__indicators--outside{bottom:26px;text-align:center;position:static;-webkit-transform:none;transform:none}.custom-theme .el-carousel__indicators--outside .el-carousel__indicator:hover button{opacity:.64}.custom-theme .el-carousel__indicators--outside button{background-color:#b4bccc;opacity:.24}.custom-theme .el-carousel__indicators--labels{left:0;right:0;-webkit-transform:none;transform:none;text-align:center}.custom-theme .el-carousel__indicators--labels .el-carousel__button{height:auto;width:auto;padding:2px 18px;font-size:12px}.custom-theme .el-carousel__indicators--labels .el-carousel__indicator{padding:6px 4px}.custom-theme .el-carousel__indicator{display:inline-block;background-color:transparent;padding:12px 4px;cursor:pointer}.custom-theme .el-carousel__indicator:hover button{opacity:.72}.custom-theme .el-carousel__indicator.is-active button{opacity:1}.custom-theme .el-carousel__button{display:block;opacity:.48;width:30px;height:2px;background-color:#fff;border:none;outline:0;padding:0;margin:0;cursor:pointer;-webkit-transition:.3s;transition:.3s}.custom-theme .carousel-arrow-left-enter,.custom-theme .carousel-arrow-left-leave-active{-webkit-transform:translateY(-50%) translateX(-10px);transform:translateY(-50%) translateX(-10px);opacity:0}.custom-theme .carousel-arrow-right-enter,.custom-theme .carousel-arrow-right-leave-active{-webkit-transform:translateY(-50%) translateX(10px);transform:translateY(-50%) translateX(10px);opacity:0}.custom-theme .el-scrollbar{overflow:hidden;position:relative}.custom-theme .el-scrollbar:active>.el-scrollbar__bar,.custom-theme .el-scrollbar:focus>.el-scrollbar__bar,.custom-theme .el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity 340ms ease-out;transition:opacity 340ms ease-out}.custom-theme .el-scrollbar__wrap{overflow:scroll;height:100%}.custom-theme .el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.custom-theme .el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(135,141,153,.3);-webkit-transition:.3s background-color;transition:.3s background-color}.custom-theme .el-scrollbar__thumb:hover{background-color:rgba(135,141,153,.5)}.custom-theme .el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity 120ms ease-out;transition:opacity 120ms ease-out}.custom-theme .el-scrollbar__bar.is-vertical{width:6px;top:2px}.custom-theme .el-scrollbar__bar.is-vertical>div{width:100%}.custom-theme .el-scrollbar__bar.is-horizontal{height:6px;left:2px}.custom-theme .el-scrollbar__bar.is-horizontal>div{height:100%}.custom-theme .el-carousel__item{position:absolute;top:0;left:0;width:100%;height:100%;display:inline-block;overflow:hidden;z-index:0}.custom-theme .el-carousel__item.is-active{z-index:2}.custom-theme .el-carousel__item.is-animating{-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.custom-theme .el-carousel__item--card{width:50%;-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.custom-theme .el-carousel__item--card.is-in-stage{cursor:pointer;z-index:1}.custom-theme .el-carousel__item--card.is-in-stage.is-hover .el-carousel__mask,.custom-theme .el-carousel__item--card.is-in-stage:hover .el-carousel__mask{opacity:.12}.custom-theme .el-carousel__item--card.is-active{z-index:2}.custom-theme .el-carousel__mask{position:absolute;width:100%;height:100%;top:0;left:0;background-color:#fff;opacity:.24;-webkit-transition:.2s;transition:.2s}.custom-theme .el-collapse{border-top:1px solid #e6ebf5;border-bottom:1px solid #e6ebf5}.custom-theme .el-collapse-item__header{height:48px;line-height:48px;background-color:#fff;color:#2d2f33;cursor:pointer;border-bottom:1px solid #e6ebf5;font-size:13px;font-weight:500;-webkit-transition:border-bottom-color .3s;transition:border-bottom-color .3s}.custom-theme .el-collapse-item__header:active,.custom-theme .el-collapse-item__header:focus:not(.focusing){outline-width:0}.custom-theme .el-collapse-item__arrow{margin-right:8px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;float:right;line-height:48px;font-weight:300}.custom-theme .el-collapse-item__wrap{will-change:height;background-color:#fff;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;border-bottom:1px solid #e6ebf5}.custom-theme .el-collapse-item__content{padding-bottom:25px;font-size:13px;color:#2d2f33;line-height:1.769230769230769}.custom-theme .el-collapse-item.is-active .el-collapse-item__header{border-bottom-color:transparent}.custom-theme .el-collapse-item.is-active .el-collapse-item__header .el-collapse-item__arrow{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.custom-theme .el-collapse-item:last-child{margin-bottom:-1px}.custom-theme .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.custom-theme .el-input::-webkit-scrollbar{z-index:11;width:6px}.custom-theme .el-input::-webkit-scrollbar:horizontal{height:6px}.custom-theme .el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.custom-theme .el-input::-webkit-scrollbar-corner{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.custom-theme .el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.custom-theme .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input__inner:hover{border-color:#b4bccc}.custom-theme .el-input__inner:focus{outline:0;border-color:#262729}.custom-theme .el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.custom-theme .el-input__suffix-inner{pointer-events:all}.custom-theme .el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s}.custom-theme .el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.custom-theme .el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.custom-theme .el-input__validateIcon{pointer-events:none}.custom-theme .el-input.is-active .el-input__inner{outline:0;border-color:#262729}.custom-theme .el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__icon{cursor:not-allowed}.custom-theme .el-input--suffix .el-input__inner{padding-right:30px}.custom-theme .el-input--prefix .el-input__inner{padding-left:30px}.custom-theme .el-input--medium{font-size:14px}.custom-theme .el-input--medium .el-input__inner{height:36px}.custom-theme .el-input--medium .el-input__icon{line-height:36px}.custom-theme .el-input--small{font-size:13px}.custom-theme .el-input--small .el-input__inner{height:32px}.custom-theme .el-input--small .el-input__icon{line-height:32px}.custom-theme .el-input--mini{font-size:12px}.custom-theme .el-input--mini .el-input__inner{height:28px}.custom-theme .el-input--mini .el-input__icon{line-height:28px}.custom-theme .el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.custom-theme .el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.custom-theme .el-input-group__append,.custom-theme .el-input-group__prepend{background-color:#f5f7fa;color:#0a76a4;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.custom-theme .el-input-group__append:focus,.custom-theme .el-input-group__prepend:focus{outline:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-select,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-select{display:inline-block;margin:-20px}.custom-theme .el-input-group__append button.el-button,.custom-theme .el-input-group__append div.el-select .el-input__inner,.custom-theme .el-input-group__append div.el-select:hover .el-input__inner,.custom-theme .el-input-group__prepend button.el-button,.custom-theme .el-input-group__prepend div.el-select .el-input__inner,.custom-theme .el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-input,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-input{font-size:inherit}.custom-theme .el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-textarea{display:inline-block;width:100%;vertical-align:bottom}.custom-theme .el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:hover{border-color:#b4bccc}.custom-theme .el-textarea__inner:focus{outline:0;border-color:#262729}.custom-theme .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-popper .popper__arrow,.custom-theme .el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.custom-theme .el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.custom-theme .el-popper .popper__arrow::after{content:\" \";border-width:6px}.custom-theme .el-popper[x-placement^=top]{margin-bottom:12px}.custom-theme .el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.custom-theme .el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.custom-theme .el-popper[x-placement^=bottom]{margin-top:12px}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.custom-theme .el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.custom-theme .el-popper[x-placement^=right]{margin-left:12px}.custom-theme .el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.custom-theme .el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.custom-theme .el-popper[x-placement^=left]{margin-right:12px}.custom-theme .el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.custom-theme .el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.custom-theme .el-cascader{display:inline-block;position:relative;font-size:14px;line-height:40px}.custom-theme .el-cascader .el-input,.custom-theme .el-cascader .el-input__inner{cursor:pointer}.custom-theme .el-cascader .el-input__icon{-webkit-transition:none;transition:none}.custom-theme .el-cascader .el-icon-arrow-down{-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-size:14px}.custom-theme .el-cascader .el-icon-arrow-down.is-reverse{-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg)}.custom-theme .el-cascader .el-icon-circle-close{z-index:2;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-cascader .el-icon-circle-close:hover{color:#878d99}.custom-theme .el-cascader__clearIcon{z-index:2;position:relative}.custom-theme .el-cascader__label{position:absolute;left:0;top:0;height:100%;padding:0 25px 0 15px;color:#5a5e66;width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:pointer;text-align:left;font-size:inherit}.custom-theme .el-cascader__label span{color:#000}.custom-theme .el-cascader--medium{font-size:14px;line-height:36px}.custom-theme .el-cascader--small{font-size:13px;line-height:32px}.custom-theme .el-cascader--mini{font-size:12px;line-height:28px}.custom-theme .el-cascader.is-disabled .el-cascader__label{z-index:2;color:#b4bccc}.custom-theme .el-cascader-menus{white-space:nowrap;background:#fff;position:absolute;margin:5px 0;z-index:2;border:solid 1px #dfe4ed;border-radius:2px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.custom-theme .el-cascader-menus .popper__arrow{-webkit-transform:translateX(-400%);transform:translateX(-400%)}.custom-theme .el-cascader-menu{display:inline-block;vertical-align:top;height:204px;overflow:auto;border-right:solid 1px #dfe4ed;background-color:#fff;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:6px 0;min-width:160px}.custom-theme .el-cascader-menu:last-child{border-right:0}.custom-theme .el-cascader-menu__item{font-size:14px;padding:8px 20px;position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#5a5e66;height:34px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:pointer}.custom-theme .el-cascader-menu__item--extensible:after{font-family:element-icons;content:\"\\e604\";font-size:14px;color:#bfcbd9;position:absolute;right:15px}.custom-theme .el-cascader-menu__item.is-disabled{color:#b4bccc;background-color:#fff;cursor:not-allowed}.custom-theme .el-cascader-menu__item.is-disabled:hover{background-color:#fff}.custom-theme .el-cascader-menu__item.is-active{color:#262729}.custom-theme .el-cascader-menu__item:hover{background-color:#f5f7fa}.custom-theme .el-cascader-menu__item.selected{color:#fff;background-color:#f5f7fa}.custom-theme .el-cascader-menu__item__keyword{font-weight:700}.custom-theme .el-cascader-menu--flexible{height:auto;max-height:180px;overflow:auto}.custom-theme .el-cascader-menu--flexible .el-cascader-menu__item{overflow:visible}.custom-theme .el-color-hue-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background-color:red;padding:0 2px}.custom-theme .el-color-hue-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,from(red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%);height:100%}.custom-theme .el-color-hue-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.custom-theme .el-color-hue-slider.is-vertical{width:12px;height:180px;padding:2px 0}.custom-theme .el-color-hue-slider.is-vertical .el-color-hue-slider__bar{background:-webkit-gradient(linear,left top,left bottom,from(red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(to bottom,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}.custom-theme .el-color-hue-slider.is-vertical .el-color-hue-slider__thumb{left:0;top:0;width:100%;height:4px}.custom-theme .el-color-svpanel{position:relative;width:280px;height:180px}.custom-theme .el-color-svpanel__black,.custom-theme .el-color-svpanel__white{position:absolute;top:0;left:0;right:0;bottom:0}.custom-theme .el-color-svpanel__white{background:-webkit-gradient(linear,left top,right top,from(#fff),to(rgba(255,255,255,0)));background:linear-gradient(to right,#fff,rgba(255,255,255,0))}.custom-theme .el-color-svpanel__black{background:-webkit-gradient(linear,left bottom,left top,from(#000),to(transparent));background:linear-gradient(to top,#000,transparent)}.custom-theme .el-color-svpanel__cursor{position:absolute}.custom-theme .el-color-svpanel__cursor>div{cursor:head;width:4px;height:4px;-webkit-box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);border-radius:50%;-webkit-transform:translate(-2px,-2px);transform:translate(-2px,-2px)}.custom-theme .el-color-alpha-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.custom-theme .el-color-alpha-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,from(rgba(255,255,255,0)),to(white));background:linear-gradient(to right,rgba(255,255,255,0) 0,#fff 100%);height:100%}.custom-theme .el-color-alpha-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.custom-theme .el-color-alpha-slider.is-vertical{width:20px;height:180px}.custom-theme .el-color-alpha-slider.is-vertical .el-color-alpha-slider__bar{background:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,0)),to(white));background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fff 100%)}.custom-theme .el-color-alpha-slider.is-vertical .el-color-alpha-slider__thumb{left:0;top:0;width:100%;height:4px}.custom-theme .el-color-dropdown{width:300px}.custom-theme .el-color-dropdown__main-wrapper{margin-bottom:6px}.custom-theme .el-color-dropdown__main-wrapper::after{content:\"\";display:table;clear:both}.custom-theme .el-color-dropdown__btns{margin-top:6px;text-align:right}.custom-theme .el-color-dropdown__value{float:left;line-height:26px;font-size:12px;color:#000;width:160px}.custom-theme .el-color-dropdown__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.custom-theme .el-color-dropdown__btn[disabled]{color:#ccc;cursor:not-allowed}.custom-theme .el-color-dropdown__btn:hover{color:#262729;border-color:#262729}.custom-theme .el-color-dropdown__link-btn{cursor:pointer;color:#262729;text-decoration:none;padding:15px;font-size:12px}.custom-theme .el-color-dropdown__link-btn:hover{color:tint(#262729,20%)}.custom-theme .el-color-picker{display:inline-block;position:relative;line-height:normal;height:40px}.custom-theme .el-color-picker.is-disabled .el-color-picker__trigger{cursor:not-allowed}.custom-theme .el-color-picker--medium{height:36px}.custom-theme .el-color-picker--medium .el-color-picker__trigger{height:36px;width:36px}.custom-theme .el-color-picker--medium .el-color-picker__mask{height:34px;width:34px}.custom-theme .el-color-picker--small{height:32px}.custom-theme .el-color-picker--small .el-color-picker__trigger{height:32px;width:32px}.custom-theme .el-color-picker--small .el-color-picker__mask{height:30px;width:30px}.custom-theme .el-color-picker--small .el-color-picker__empty,.custom-theme .el-color-picker--small .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.custom-theme .el-color-picker--mini{height:28px}.custom-theme .el-color-picker--mini .el-color-picker__trigger{height:28px;width:28px}.custom-theme .el-color-picker--mini .el-color-picker__mask{height:26px;width:26px}.custom-theme .el-color-picker--mini .el-color-picker__empty,.custom-theme .el-color-picker--mini .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.custom-theme .el-color-picker__mask{height:38px;width:38px;border-radius:4px;position:absolute;top:1px;left:1px;z-index:1;cursor:not-allowed;background-color:rgba(255,255,255,.7)}.custom-theme .el-color-picker__trigger{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;height:40px;width:40px;padding:4px;border:1px solid #e6e6e6;border-radius:4px;font-size:0;position:relative;cursor:pointer}.custom-theme .el-color-picker__color{position:relative;display:block;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #999;border-radius:2px;width:100%;height:100%;text-align:center}.custom-theme .el-color-picker__color.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.custom-theme .el-color-picker__color-inner{position:absolute;left:0;top:0;right:0;bottom:0}.custom-theme .el-color-picker__empty{font-size:12px;color:#999;position:absolute;top:50%;left:50%;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}.custom-theme .el-color-picker__icon{display:inline-block;position:absolute;width:100%;top:50%;left:50%;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0);color:#fff;text-align:center;font-size:12px}.custom-theme .el-color-picker__panel{position:absolute;z-index:10;padding:6px;background-color:#fff;border:1px solid #e6ebf5;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.custom-theme .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.custom-theme .el-input::-webkit-scrollbar{z-index:11;width:6px}.custom-theme .el-input::-webkit-scrollbar:horizontal{height:6px}.custom-theme .el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.custom-theme .el-input::-webkit-scrollbar-corner{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track{background:#fff}.custom-theme .el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.custom-theme .el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.custom-theme .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input__inner:hover{border-color:#b4bccc}.custom-theme .el-input__inner:focus{outline:0;border-color:#262729}.custom-theme .el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.custom-theme .el-input__suffix-inner{pointer-events:all}.custom-theme .el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:#b4bccc;-webkit-transition:all .3s;transition:all .3s}.custom-theme .el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.custom-theme .el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.custom-theme .el-input__validateIcon{pointer-events:none}.custom-theme .el-input.is-active .el-input__inner{outline:0;border-color:#262729}.custom-theme .el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.custom-theme .el-input.is-disabled .el-input__icon{cursor:not-allowed}.custom-theme .el-input--suffix .el-input__inner{padding-right:30px}.custom-theme .el-input--prefix .el-input__inner{padding-left:30px}.custom-theme .el-input--medium{font-size:14px}.custom-theme .el-input--medium .el-input__inner{height:36px}.custom-theme .el-input--medium .el-input__icon{line-height:36px}.custom-theme .el-input--small{font-size:13px}.custom-theme .el-input--small .el-input__inner{height:32px}.custom-theme .el-input--small .el-input__icon{line-height:32px}.custom-theme .el-input--mini{font-size:12px}.custom-theme .el-input--mini .el-input__inner{height:28px}.custom-theme .el-input--mini .el-input__icon{line-height:28px}.custom-theme .el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.custom-theme .el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.custom-theme .el-input-group__append,.custom-theme .el-input-group__prepend{background-color:#f5f7fa;color:#0a76a4;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.custom-theme .el-input-group__append:focus,.custom-theme .el-input-group__prepend:focus{outline:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-select,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-select{display:inline-block;margin:-20px}.custom-theme .el-input-group__append button.el-button,.custom-theme .el-input-group__append div.el-select .el-input__inner,.custom-theme .el-input-group__append div.el-select:hover .el-input__inner,.custom-theme .el-input-group__prepend button.el-button,.custom-theme .el-input-group__prepend div.el-select .el-input__inner,.custom-theme .el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.custom-theme .el-input-group__append .el-button,.custom-theme .el-input-group__append .el-input,.custom-theme .el-input-group__prepend .el-button,.custom-theme .el-input-group__prepend .el-input{font-size:inherit}.custom-theme .el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-textarea{display:inline-block;width:100%;vertical-align:bottom}.custom-theme .el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.custom-theme .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-textarea__inner:hover{border-color:#b4bccc}.custom-theme .el-textarea__inner:focus{outline:0;border-color:#262729}.custom-theme .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#b4bccc}.custom-theme .el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.custom-theme .el-button{display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;background:#fff;border:1px solid #d8dce5;border-color:#d8dce5;color:#5a5e66;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:.1s;transition:.1s;font-weight:500;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:14px;border-radius:4px}.custom-theme .el-button+.el-button{margin-left:10px}.custom-theme .el-button.is-round{padding:12px 20px}.custom-theme .el-button:focus,.custom-theme .el-button:hover{color:#262729;border-color:#bebebf;background-color:#e9e9ea}.custom-theme .el-button:active{color:#222325;border-color:#222325;outline:0}.custom-theme .el-button::-moz-focus-inner{border:0}.custom-theme .el-button [class*=el-icon-]+span{margin-left:5px}.custom-theme .el-button.is-plain:focus,.custom-theme .el-button.is-plain:hover{background:#fff;border-color:#262729;color:#262729}.custom-theme .el-button.is-plain:active{background:#fff;border-color:#222325;color:#222325;outline:0}.custom-theme .el-button.is-active{color:#222325;border-color:#222325}.custom-theme .el-button.is-disabled,.custom-theme .el-button.is-disabled:focus,.custom-theme .el-button.is-disabled:hover{color:#b4bccc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#e6ebf5}.custom-theme .el-button.is-disabled.el-button--text{background-color:transparent}.custom-theme .el-button.is-disabled.is-plain,.custom-theme .el-button.is-disabled.is-plain:focus,.custom-theme .el-button.is-disabled.is-plain:hover{background-color:#fff;border-color:#e6ebf5;color:#b4bccc}.custom-theme .el-button.is-loading{position:relative;pointer-events:none}.custom-theme .el-button.is-loading:before{pointer-events:none;content:'';position:absolute;left:-1px;top:-1px;right:-1px;bottom:-1px;border-radius:inherit;background-color:rgba(255,255,255,.35)}.custom-theme .el-button.is-round{border-radius:20px;padding:12px 23px}.custom-theme .el-button--primary{color:#fff;background-color:#262729;border-color:#262729}.custom-theme .el-button--primary:focus,.custom-theme .el-button--primary:hover{background:#515254;border-color:#515254;color:#fff}.custom-theme .el-button--primary:active{background:#222325;border-color:#222325;color:#fff;outline:0}.custom-theme .el-button--primary.is-active{background:#222325;border-color:#222325;color:#fff}.custom-theme .el-button--primary.is-disabled,.custom-theme .el-button--primary.is-disabled:active,.custom-theme .el-button--primary.is-disabled:focus,.custom-theme .el-button--primary.is-disabled:hover{color:#fff;background-color:#939394;border-color:#939394}.custom-theme .el-button--primary.is-plain{color:#262729;background:#e9e9ea;border-color:#a8a9a9}.custom-theme .el-button--primary.is-plain:focus,.custom-theme .el-button--primary.is-plain:hover{background:#262729;border-color:#262729;color:#fff}.custom-theme .el-button--primary.is-plain:active{background:#222325;border-color:#222325;color:#fff;outline:0}.custom-theme .el-button--primary.is-plain.is-disabled,.custom-theme .el-button--primary.is-plain.is-disabled:active,.custom-theme .el-button--primary.is-plain.is-disabled:focus,.custom-theme .el-button--primary.is-plain.is-disabled:hover{color:#7d7d7f;background-color:#e9e9ea;border-color:#d4d4d4}.custom-theme .el-button--success{color:#fff;background-color:#409167;border-color:#409167}.custom-theme .el-button--success:focus,.custom-theme .el-button--success:hover{background:#66a785;border-color:#66a785;color:#fff}.custom-theme .el-button--success:active{background:#3a835d;border-color:#3a835d;color:#fff;outline:0}.custom-theme .el-button--success.is-active{background:#3a835d;border-color:#3a835d;color:#fff}.custom-theme .el-button--success.is-disabled,.custom-theme .el-button--success.is-disabled:active,.custom-theme .el-button--success.is-disabled:focus,.custom-theme .el-button--success.is-disabled:hover{color:#fff;background-color:#a0c8b3;border-color:#a0c8b3}.custom-theme .el-button--success.is-plain{color:#409167;background:#ecf4f0;border-color:#b3d3c2}.custom-theme .el-button--success.is-plain:focus,.custom-theme .el-button--success.is-plain:hover{background:#409167;border-color:#409167;color:#fff}.custom-theme .el-button--success.is-plain:active{background:#3a835d;border-color:#3a835d;color:#fff;outline:0}.custom-theme .el-button--success.is-plain.is-disabled,.custom-theme .el-button--success.is-plain.is-disabled:active,.custom-theme .el-button--success.is-plain.is-disabled:focus,.custom-theme .el-button--success.is-plain.is-disabled:hover{color:#8cbda4;background-color:#ecf4f0;border-color:#d9e9e1}.custom-theme .el-button--warning{color:#fff;background-color:#9da408;border-color:#9da408}.custom-theme .el-button--warning:focus,.custom-theme .el-button--warning:hover{background:#b1b639;border-color:#b1b639;color:#fff}.custom-theme .el-button--warning:active{background:#8d9407;border-color:#8d9407;color:#fff;outline:0}.custom-theme .el-button--warning.is-active{background:#8d9407;border-color:#8d9407;color:#fff}.custom-theme .el-button--warning.is-disabled,.custom-theme .el-button--warning.is-disabled:active,.custom-theme .el-button--warning.is-disabled:focus,.custom-theme .el-button--warning.is-disabled:hover{color:#fff;background-color:#ced284;border-color:#ced284}.custom-theme .el-button--warning.is-plain{color:#9da408;background:#f5f6e6;border-color:#d8db9c}.custom-theme .el-button--warning.is-plain:focus,.custom-theme .el-button--warning.is-plain:hover{background:#9da408;border-color:#9da408;color:#fff}.custom-theme .el-button--warning.is-plain:active{background:#8d9407;border-color:#8d9407;color:#fff;outline:0}.custom-theme .el-button--warning.is-plain.is-disabled,.custom-theme .el-button--warning.is-plain.is-disabled:active,.custom-theme .el-button--warning.is-plain.is-disabled:focus,.custom-theme .el-button--warning.is-plain.is-disabled:hover{color:#c4c86b;background-color:#f5f6e6;border-color:#ebedce}.custom-theme .el-button--danger{color:#fff;background-color:#b3450e;border-color:#b3450e}.custom-theme .el-button--danger:focus,.custom-theme .el-button--danger:hover{background:#c26a3e;border-color:#c26a3e;color:#fff}.custom-theme .el-button--danger:active{background:#a13e0d;border-color:#a13e0d;color:#fff;outline:0}.custom-theme .el-button--danger.is-active{background:#a13e0d;border-color:#a13e0d;color:#fff}.custom-theme .el-button--danger.is-disabled,.custom-theme .el-button--danger.is-disabled:active,.custom-theme .el-button--danger.is-disabled:focus,.custom-theme .el-button--danger.is-disabled:hover{color:#fff;background-color:#d9a287;border-color:#d9a287}.custom-theme .el-button--danger.is-plain{color:#b3450e;background:#f7ece7;border-color:#e1b59f}.custom-theme .el-button--danger.is-plain:focus,.custom-theme .el-button--danger.is-plain:hover{background:#b3450e;border-color:#b3450e;color:#fff}.custom-theme .el-button--danger.is-plain:active{background:#a13e0d;border-color:#a13e0d;color:#fff;outline:0}.custom-theme .el-button--danger.is-plain.is-disabled,.custom-theme .el-button--danger.is-plain.is-disabled:active,.custom-theme .el-button--danger.is-plain.is-disabled:focus,.custom-theme .el-button--danger.is-plain.is-disabled:hover{color:#d18f6e;background-color:#f7ece7;border-color:#f0dacf}.custom-theme .el-button--info{color:#fff;background-color:#0a76a4;border-color:#0a76a4}.custom-theme .el-button--info:focus,.custom-theme .el-button--info:hover{background:#3b91b6;border-color:#3b91b6;color:#fff}.custom-theme .el-button--info:active{background:#096a94;border-color:#096a94;color:#fff;outline:0}.custom-theme .el-button--info.is-active{background:#096a94;border-color:#096a94;color:#fff}.custom-theme .el-button--info.is-disabled,.custom-theme .el-button--info.is-disabled:active,.custom-theme .el-button--info.is-disabled:focus,.custom-theme .el-button--info.is-disabled:hover{color:#fff;background-color:#85bbd2;border-color:#85bbd2}.custom-theme .el-button--info.is-plain{color:#0a76a4;background:#e7f1f6;border-color:#9dc8db}.custom-theme .el-button--info.is-plain:focus,.custom-theme .el-button--info.is-plain:hover{background:#0a76a4;border-color:#0a76a4;color:#fff}.custom-theme .el-button--info.is-plain:active{background:#096a94;border-color:#096a94;color:#fff;outline:0}.custom-theme .el-button--info.is-plain.is-disabled,.custom-theme .el-button--info.is-plain.is-disabled:active,.custom-theme .el-button--info.is-plain.is-disabled:focus,.custom-theme .el-button--info.is-plain.is-disabled:hover{color:#6cadc8;background-color:#e7f1f6;border-color:#cee4ed}.custom-theme .el-button--medium{padding:10px 20px;font-size:14px;border-radius:4px}.custom-theme .el-button--medium.is-round{padding:10px 20px}.custom-theme .el-button--small{padding:9px 15px;font-size:12px;border-radius:3px}.custom-theme .el-button--small.is-round{padding:9px 15px}.custom-theme .el-button--mini{padding:7px 15px;font-size:12px;border-radius:3px}.custom-theme .el-button--mini.is-round{padding:7px 15px}.custom-theme .el-button--text{border:none;color:#262729;background:0 0;padding-left:0;padding-right:0}.custom-theme .el-button--text:focus,.custom-theme .el-button--text:hover{color:#515254;border-color:transparent;background-color:transparent}.custom-theme .el-button--text:active{color:#222325;border-color:transparent;background-color:transparent}.custom-theme .el-button-group{display:inline-block;vertical-align:middle}.custom-theme .el-button-group::after,.custom-theme .el-button-group::before{display:table;content:\"\"}.custom-theme .el-button-group::after{clear:both}.custom-theme .el-button-group .el-button{float:left;position:relative}.custom-theme .el-button-group .el-button+.el-button{margin-left:0}.custom-theme .el-button-group .el-button:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.custom-theme .el-button-group .el-button:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.custom-theme .el-button-group .el-button:not(:first-child):not(:last-child){border-radius:0}.custom-theme .el-button-group .el-button:not(:last-child){margin-right:-1px}.custom-theme .el-button-group .el-button:active,.custom-theme .el-button-group .el-button:focus,.custom-theme .el-button-group .el-button:hover{z-index:1}.custom-theme .el-button-group .el-button.is-active{z-index:1}.custom-theme .el-button-group .el-button--primary:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--primary:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--primary:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--success:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--warning:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--danger:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:first-child{border-right-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:last-child{border-left-color:rgba(255,255,255,.5)}.custom-theme .el-button-group .el-button--info:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.custom-theme .el-checkbox{color:#5a5e66;font-weight:500;font-size:14px;position:relative;cursor:pointer;display:inline-block;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-theme .el-checkbox.is-bordered{padding:9px 20px 9px 10px;border-radius:4px;border:1px solid #d8dce5}.custom-theme .el-checkbox.is-bordered.is-checked{border-color:#262729}.custom-theme .el-checkbox.is-bordered.is-disabled{border-color:#e6ebf5;cursor:not-allowed}.custom-theme .el-checkbox.is-bordered+.el-checkbox.is-bordered{margin-left:10px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium{padding:7px 20px 7px 10px;border-radius:4px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__label{line-height:17px;font-size:14px}.custom-theme .el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__inner{height:14px;width:14px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small{padding:3px 15px 7px 10px;border-radius:3px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{line-height:15px;font-size:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner{height:12px;width:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner::after{height:6px;width:2px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini{padding:1px 15px 5px 10px;border-radius:3px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__label{line-height:12px;font-size:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner{height:12px;width:12px}.custom-theme .el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner::after{height:6px;width:2px}.custom-theme .el-checkbox__input{white-space:nowrap;cursor:pointer;outline:0;display:inline-block;line-height:1;position:relative;vertical-align:middle}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5;cursor:not-allowed}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner::after{cursor:not-allowed;border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled .el-checkbox__inner+.el-checkbox__label{cursor:not-allowed}.custom-theme .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5}.custom-theme .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner::after{border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5}.custom-theme .el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner::before{background-color:#b4bccc;border-color:#b4bccc}.custom-theme .el-checkbox__input.is-disabled+span.el-checkbox__label{color:#b4bccc;cursor:not-allowed}.custom-theme .el-checkbox__input.is-checked .el-checkbox__inner{background-color:#262729;border-color:#262729}.custom-theme .el-checkbox__input.is-checked .el-checkbox__inner::after{-webkit-transform:rotate(45deg) scaleY(1);transform:rotate(45deg) scaleY(1)}.custom-theme .el-checkbox__input.is-checked+.el-checkbox__label{color:#262729}.custom-theme .el-checkbox__input.is-focus .el-checkbox__inner{border-color:#262729}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#262729;border-color:#262729}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner::before{content:'';position:absolute;display:block;background-color:#fff;height:2px;-webkit-transform:scale(.5);transform:scale(.5);left:0;right:0;top:5px}.custom-theme .el-checkbox__input.is-indeterminate .el-checkbox__inner::after{display:none}.custom-theme .el-checkbox__inner{display:inline-block;position:relative;border:1px solid #d8dce5;border-radius:2px;-webkit-box-sizing:border-box;box-sizing:border-box;width:14px;height:14px;background-color:#fff;z-index:1;-webkit-transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46);transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46)}.custom-theme .el-checkbox__inner:hover{border-color:#262729}.custom-theme .el-checkbox__inner::after{-webkit-box-sizing:content-box;box-sizing:content-box;content:\"\";border:1px solid #fff;border-left:0;border-top:0;height:7px;left:4px;position:absolute;top:1px;-webkit-transform:rotate(45deg) scaleY(0);transform:rotate(45deg) scaleY(0);width:3px;-webkit-transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms,-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;-webkit-transform-origin:center;transform-origin:center}.custom-theme .el-checkbox__original{opacity:0;outline:0;position:absolute;margin:0;width:0;height:0;left:-999px}.custom-theme .el-checkbox__label{display:inline-block;padding-left:10px;line-height:19px;font-size:14px}.custom-theme .el-checkbox+.el-checkbox{margin-left:30px}.custom-theme .el-checkbox-button{position:relative;display:inline-block}.custom-theme .el-checkbox-button__inner{display:inline-block;line-height:1;font-weight:500;white-space:nowrap;vertical-align:middle;cursor:pointer;background:#fff;border:1px solid #d8dce5;border-left:0;color:#5a5e66;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;position:relative;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:14px;border-radius:0}.custom-theme .el-checkbox-button__inner.is-round{padding:12px 20px}.custom-theme .el-checkbox-button__inner:hover{color:#262729}.custom-theme .el-checkbox-button__inner [class*=el-icon-]{line-height:.9}.custom-theme .el-checkbox-button__inner [class*=el-icon-]+span{margin-left:5px}.custom-theme .el-checkbox-button__original{opacity:0;outline:0;position:absolute;margin:0;left:-999px}.custom-theme .el-checkbox-button.is-checked .el-checkbox-button__inner{color:#fff;background-color:#262729;border-color:#262729;-webkit-box-shadow:-1px 0 0 0 #7d7d7f;box-shadow:-1px 0 0 0 #7d7d7f}.custom-theme .el-checkbox-button.is-disabled .el-checkbox-button__inner{color:#b4bccc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#e6ebf5;-webkit-box-shadow:none;box-shadow:none}.custom-theme .el-checkbox-button:first-child .el-checkbox-button__inner{border-left:1px solid #d8dce5;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.custom-theme .el-checkbox-button.is-focus .el-checkbox-button__inner{border-color:#262729}.custom-theme .el-checkbox-button:last-child .el-checkbox-button__inner{border-radius:0 4px 4px 0}.custom-theme .el-checkbox-button--medium .el-checkbox-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.custom-theme .el-checkbox-button--medium .el-checkbox-button__inner.is-round{padding:10px 20px}.custom-theme .el-checkbox-button--small .el-checkbox-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.custom-theme .el-checkbox-button--small .el-checkbox-button__inner.is-round{padding:9px 15px}.custom-theme .el-checkbox-button--mini .el-checkbox-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.custom-theme .el-checkbox-button--mini .el-checkbox-button__inner.is-round{padding:7px 15px}.custom-theme .el-checkbox-group{font-size:0}.custom-theme .el-transfer{font-size:14px}.custom-theme .el-transfer__buttons{display:inline-block;vertical-align:middle;padding:0 30px}.custom-theme .el-transfer__button{display:block;margin:0 auto;padding:10px;border-radius:50%;color:#fff;background-color:#262729;font-size:0}.custom-theme .el-transfer__button.is-with-texts{border-radius:4px}.custom-theme .el-transfer__button.is-disabled{border:1px solid #d8dce5;background-color:#f5f7fa;color:#b4bccc}.custom-theme .el-transfer__button.is-disabled:hover{border:1px solid #d8dce5;background-color:#f5f7fa;color:#b4bccc}.custom-theme .el-transfer__button:first-child{margin-bottom:10px}.custom-theme .el-transfer__button:nth-child(2){margin:0}.custom-theme .el-transfer__button i,.custom-theme .el-transfer__button span{font-size:14px}.custom-theme .el-transfer__button [class*=el-icon-]+span{margin-left:0}.custom-theme .el-transfer-panel{border:1px solid #e6ebf5;border-radius:4px;overflow:hidden;background:#fff;display:inline-block;vertical-align:middle;width:200px;-webkit-box-sizing:border-box;box-sizing:border-box;position:relative}.custom-theme .el-transfer-panel__body{height:246px}.custom-theme .el-transfer-panel__body.is-with-footer{padding-bottom:40px}.custom-theme .el-transfer-panel__list{margin:0;padding:6px 0;list-style:none;height:246px;overflow:auto;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-transfer-panel__list.is-filterable{height:194px;padding-top:0}.custom-theme .el-transfer-panel__item{height:30px;line-height:30px;padding-left:15px;display:block}.custom-theme .el-transfer-panel__item+.el-transfer-panel__item{margin-left:0}.custom-theme .el-transfer-panel__item.el-checkbox{color:#5a5e66}.custom-theme .el-transfer-panel__item:hover{color:#262729}.custom-theme .el-transfer-panel__item.el-checkbox .el-checkbox__label{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;-webkit-box-sizing:border-box;box-sizing:border-box;padding-left:24px;line-height:30px}.custom-theme .el-transfer-panel__item .el-checkbox__input{position:absolute;top:8px}.custom-theme .el-transfer-panel__filter{text-align:center;margin:15px;-webkit-box-sizing:border-box;box-sizing:border-box;display:block;width:auto}.custom-theme .el-transfer-panel__filter .el-input__inner{height:32px;width:100%;font-size:12px;display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:16px;padding-right:10px;padding-left:30px}.custom-theme .el-transfer-panel__filter .el-input__icon{margin-left:5px}.custom-theme .el-transfer-panel__filter .el-icon-circle-close{cursor:pointer}.custom-theme .el-transfer-panel .el-transfer-panel__header{height:40px;line-height:40px;background:#f5f7fa;margin:0;padding-left:15px;border-bottom:1px solid #e6ebf5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#000}.custom-theme .el-transfer-panel .el-transfer-panel__header .el-checkbox{display:block;line-height:40px}.custom-theme .el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label{font-size:16px;color:#2d2f33;font-weight:400}.custom-theme .el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label span{position:absolute;right:15px;color:#878d99;font-size:12px;font-weight:400}.custom-theme .el-transfer-panel .el-transfer-panel__footer{height:40px;background:#fff;margin:0;padding:0;border-top:1px solid #e6ebf5;position:absolute;bottom:0;left:0;width:100%;z-index:1}.custom-theme .el-transfer-panel .el-transfer-panel__footer::after{display:inline-block;content:\"\";height:100%;vertical-align:middle}.custom-theme .el-transfer-panel .el-transfer-panel__footer .el-checkbox{padding-left:20px;color:#5a5e66}.custom-theme .el-transfer-panel .el-transfer-panel__empty{margin:0;height:30px;line-height:30px;padding:6px 15px 0;color:#878d99}.custom-theme .el-transfer-panel .el-checkbox__label{padding-left:8px}.custom-theme .el-transfer-panel .el-checkbox__inner{height:14px;width:14px;border-radius:3px}.custom-theme .el-transfer-panel .el-checkbox__inner::after{height:6px;width:3px;left:4px}.custom-theme .el-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-flex:1;-ms-flex:1;flex:1;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-container.is-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.custom-theme .el-header{padding:0 20px;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-aside{overflow:auto;-webkit-box-sizing:border-box;box-sizing:border-box}.custom-theme .el-main{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow:auto;-webkit-box-sizing:border-box;box-sizing:border-box;padding:20px}.custom-theme .el-footer{padding:0 20px;-webkit-box-sizing:border-box;box-sizing:border-box}"
  },
  {
    "path": "frontend/src/components/Breadcrumb/index.vue",
    "content": "<template>\n  <el-breadcrumb class=\"app-breadcrumb\" separator=\"/\">\n    <transition-group name=\"breadcrumb\">\n      <el-breadcrumb-item v-for=\"(item,index) in levelList\" :key=\"item.path\">\n        <span v-if=\"item.redirect==='noRedirect'||index==levelList.length-1\" class=\"no-redirect\">{{ generateTitle(item.meta.title) }}</span>\n        <a v-else @click.prevent=\"handleLink(item)\">{{ generateTitle(item.meta.title) }}</a>\n      </el-breadcrumb-item>\n    </transition-group>\n  </el-breadcrumb>\n</template>\n\n<script>\nimport { generateTitle } from '@/utils/i18n'\nimport pathToRegexp from 'path-to-regexp'\n\nexport default {\n  data() {\n    return {\n      levelList: null\n    }\n  },\n  watch: {\n    $route(route) {\n      // if you go to the redirect page, do not update the breadcrumbs\n      if (route.path.startsWith('/redirect/')) {\n        return\n      }\n      this.getBreadcrumb()\n    }\n  },\n  created() {\n    this.getBreadcrumb()\n  },\n  methods: {\n    generateTitle,\n    getBreadcrumb() {\n      // only show routes with meta.title\n      let matched = this.$route.matched.filter(item => item.meta && item.meta.title)\n      const first = matched[0]\n\n      if (!this.isDashboard(first)) {\n        matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)\n      }\n\n      this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)\n    },\n    isDashboard(route) {\n      const name = route && route.name\n      if (!name) {\n        return false\n      }\n      return name.trim().toLocaleLowerCase() === 'dashboard'.toLocaleLowerCase()\n    },\n    pathCompile(path) {\n      // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561\n      const { params } = this.$route\n      var toPath = pathToRegexp.compile(path)\n      return toPath(params)\n    },\n    handleLink(item) {\n      const { redirect, path } = item\n      if (redirect) {\n        this.$router.push(redirect)\n        return\n      }\n      this.$router.push(this.pathCompile(path))\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.app-breadcrumb.el-breadcrumb {\n  display: inline-block;\n  font-size: 14px;\n  line-height: 50px;\n  margin-left: 8px;\n\n  .no-redirect {\n    color: #97a8be;\n    cursor: text;\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/Charts/keyboard.vue",
    "content": "<template>\n  <div :id=\"id\" :class=\"className\" :style=\"{height:height,width:width}\" />\n</template>\n\n<script>\nimport echarts from 'echarts'\nimport resize from './mixins/resize'\n\nexport default {\n  mixins: [resize],\n  props: {\n    className: {\n      type: String,\n      default: 'chart'\n    },\n    id: {\n      type: String,\n      default: 'chart'\n    },\n    width: {\n      type: String,\n      default: '200px'\n    },\n    height: {\n      type: String,\n      default: '200px'\n    }\n  },\n  data() {\n    return {\n      chart: null\n    }\n  },\n  mounted() {\n    this.initChart()\n  },\n  beforeDestroy() {\n    if (!this.chart) {\n      return\n    }\n    this.chart.dispose()\n    this.chart = null\n  },\n  methods: {\n    initChart() {\n      this.chart = echarts.init(document.getElementById(this.id))\n\n      const xAxisData = []\n      const data = []\n      const data2 = []\n      for (let i = 0; i < 50; i++) {\n        xAxisData.push(i)\n        data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5)\n        data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3)\n      }\n      this.chart.setOption({\n        backgroundColor: '#08263a',\n        grid: {\n          left: '5%',\n          right: '5%'\n        },\n        xAxis: [{\n          show: false,\n          data: xAxisData\n        }, {\n          show: false,\n          data: xAxisData\n        }],\n        visualMap: {\n          show: false,\n          min: 0,\n          max: 50,\n          dimension: 0,\n          inRange: {\n            color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055']\n          }\n        },\n        yAxis: {\n          axisLine: {\n            show: false\n          },\n          axisLabel: {\n            textStyle: {\n              color: '#4a657a'\n            }\n          },\n          splitLine: {\n            show: true,\n            lineStyle: {\n              color: '#08263f'\n            }\n          },\n          axisTick: {\n            show: false\n          }\n        },\n        series: [{\n          name: 'back',\n          type: 'bar',\n          data: data2,\n          z: 1,\n          itemStyle: {\n            normal: {\n              opacity: 0.4,\n              barBorderRadius: 5,\n              shadowBlur: 3,\n              shadowColor: '#111'\n            }\n          }\n        }, {\n          name: 'Simulate Shadow',\n          type: 'line',\n          data,\n          z: 2,\n          showSymbol: false,\n          animationDelay: 0,\n          animationEasing: 'linear',\n          animationDuration: 1200,\n          lineStyle: {\n            normal: {\n              color: 'transparent'\n            }\n          },\n          areaStyle: {\n            normal: {\n              color: '#08263a',\n              shadowBlur: 50,\n              shadowColor: '#000'\n            }\n          }\n        }, {\n          name: 'front',\n          type: 'bar',\n          data,\n          xAxisIndex: 1,\n          z: 3,\n          itemStyle: {\n            normal: {\n              barBorderRadius: 5\n            }\n          }\n        }],\n        animationEasing: 'elasticOut',\n        animationEasingUpdate: 'elasticOut',\n        animationDelay(idx) {\n          return idx * 20\n        },\n        animationDelayUpdate(idx) {\n          return idx * 20\n        }\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/components/Charts/lineMarker.vue",
    "content": "<template>\n  <div :id=\"id\" :class=\"className\" :style=\"{height:height,width:width}\" />\n</template>\n\n<script>\nimport echarts from 'echarts'\nimport resize from './mixins/resize'\n\nexport default {\n  mixins: [resize],\n  props: {\n    className: {\n      type: String,\n      default: 'chart'\n    },\n    id: {\n      type: String,\n      default: 'chart'\n    },\n    width: {\n      type: String,\n      default: '200px'\n    },\n    height: {\n      type: String,\n      default: '200px'\n    }\n  },\n  data() {\n    return {\n      chart: null\n    }\n  },\n  mounted() {\n    this.initChart()\n  },\n  beforeDestroy() {\n    if (!this.chart) {\n      return\n    }\n    this.chart.dispose()\n    this.chart = null\n  },\n  methods: {\n    initChart() {\n      this.chart = echarts.init(document.getElementById(this.id))\n\n      this.chart.setOption({\n        backgroundColor: '#394056',\n        title: {\n          top: 20,\n          text: 'Requests',\n          textStyle: {\n            fontWeight: 'normal',\n            fontSize: 16,\n            color: '#F1F1F3'\n          },\n          left: '1%'\n        },\n        tooltip: {\n          trigger: 'axis',\n          axisPointer: {\n            lineStyle: {\n              color: '#57617B'\n            }\n          }\n        },\n        legend: {\n          top: 20,\n          icon: 'rect',\n          itemWidth: 14,\n          itemHeight: 5,\n          itemGap: 13,\n          data: ['CMCC', 'CTCC', 'CUCC'],\n          right: '4%',\n          textStyle: {\n            fontSize: 12,\n            color: '#F1F1F3'\n          }\n        },\n        grid: {\n          top: 100,\n          left: '2%',\n          right: '2%',\n          bottom: '2%',\n          containLabel: true\n        },\n        xAxis: [{\n          type: 'category',\n          boundaryGap: false,\n          axisLine: {\n            lineStyle: {\n              color: '#57617B'\n            }\n          },\n          data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55']\n        }],\n        yAxis: [{\n          type: 'value',\n          name: '(%)',\n          axisTick: {\n            show: false\n          },\n          axisLine: {\n            lineStyle: {\n              color: '#57617B'\n            }\n          },\n          axisLabel: {\n            margin: 10,\n            textStyle: {\n              fontSize: 14\n            }\n          },\n          splitLine: {\n            lineStyle: {\n              color: '#57617B'\n            }\n          }\n        }],\n        series: [{\n          name: 'CMCC',\n          type: 'line',\n          smooth: true,\n          symbol: 'circle',\n          symbolSize: 5,\n          showSymbol: false,\n          lineStyle: {\n            normal: {\n              width: 1\n            }\n          },\n          areaStyle: {\n            normal: {\n              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{\n                offset: 0,\n                color: 'rgba(137, 189, 27, 0.3)'\n              }, {\n                offset: 0.8,\n                color: 'rgba(137, 189, 27, 0)'\n              }], false),\n              shadowColor: 'rgba(0, 0, 0, 0.1)',\n              shadowBlur: 10\n            }\n          },\n          itemStyle: {\n            normal: {\n              color: 'rgb(137,189,27)',\n              borderColor: 'rgba(137,189,2,0.27)',\n              borderWidth: 12\n\n            }\n          },\n          data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]\n        }, {\n          name: 'CTCC',\n          type: 'line',\n          smooth: true,\n          symbol: 'circle',\n          symbolSize: 5,\n          showSymbol: false,\n          lineStyle: {\n            normal: {\n              width: 1\n            }\n          },\n          areaStyle: {\n            normal: {\n              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{\n                offset: 0,\n                color: 'rgba(0, 136, 212, 0.3)'\n              }, {\n                offset: 0.8,\n                color: 'rgba(0, 136, 212, 0)'\n              }], false),\n              shadowColor: 'rgba(0, 0, 0, 0.1)',\n              shadowBlur: 10\n            }\n          },\n          itemStyle: {\n            normal: {\n              color: 'rgb(0,136,212)',\n              borderColor: 'rgba(0,136,212,0.2)',\n              borderWidth: 12\n\n            }\n          },\n          data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]\n        }, {\n          name: 'CUCC',\n          type: 'line',\n          smooth: true,\n          symbol: 'circle',\n          symbolSize: 5,\n          showSymbol: false,\n          lineStyle: {\n            normal: {\n              width: 1\n            }\n          },\n          areaStyle: {\n            normal: {\n              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{\n                offset: 0,\n                color: 'rgba(219, 50, 51, 0.3)'\n              }, {\n                offset: 0.8,\n                color: 'rgba(219, 50, 51, 0)'\n              }], false),\n              shadowColor: 'rgba(0, 0, 0, 0.1)',\n              shadowBlur: 10\n            }\n          },\n          itemStyle: {\n            normal: {\n              color: 'rgb(219,50,51)',\n              borderColor: 'rgba(219,50,51,0.2)',\n              borderWidth: 12\n            }\n          },\n          data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122]\n        }]\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/components/Charts/mixChart.vue",
    "content": "<template>\n  <div :id=\"id\" :class=\"className\" :style=\"{height:height,width:width}\" />\n</template>\n\n<script>\nimport echarts from 'echarts'\nimport resize from './mixins/resize'\n\nexport default {\n  mixins: [resize],\n  props: {\n    className: {\n      type: String,\n      default: 'chart'\n    },\n    id: {\n      type: String,\n      default: 'chart'\n    },\n    width: {\n      type: String,\n      default: '200px'\n    },\n    height: {\n      type: String,\n      default: '200px'\n    }\n  },\n  data() {\n    return {\n      chart: null\n    }\n  },\n  mounted() {\n    this.initChart()\n  },\n  beforeDestroy() {\n    if (!this.chart) {\n      return\n    }\n    this.chart.dispose()\n    this.chart = null\n  },\n  methods: {\n    initChart() {\n      this.chart = echarts.init(document.getElementById(this.id))\n      const xData = (function() {\n        const data = []\n        for (let i = 1; i < 13; i++) {\n          data.push(i + 'month')\n        }\n        return data\n      }())\n      this.chart.setOption({\n        backgroundColor: '#344b58',\n        title: {\n          text: 'statistics',\n          x: '20',\n          top: '20',\n          textStyle: {\n            color: '#fff',\n            fontSize: '22'\n          },\n          subtextStyle: {\n            color: '#90979c',\n            fontSize: '16'\n          }\n        },\n        tooltip: {\n          trigger: 'axis',\n          axisPointer: {\n            textStyle: {\n              color: '#fff'\n            }\n          }\n        },\n        grid: {\n          left: '5%',\n          right: '5%',\n          borderWidth: 0,\n          top: 150,\n          bottom: 95,\n          textStyle: {\n            color: '#fff'\n          }\n        },\n        legend: {\n          x: '5%',\n          top: '10%',\n          textStyle: {\n            color: '#90979c'\n          },\n          data: ['female', 'male', 'average']\n        },\n        calculable: true,\n        xAxis: [{\n          type: 'category',\n          axisLine: {\n            lineStyle: {\n              color: '#90979c'\n            }\n          },\n          splitLine: {\n            show: false\n          },\n          axisTick: {\n            show: false\n          },\n          splitArea: {\n            show: false\n          },\n          axisLabel: {\n            interval: 0\n\n          },\n          data: xData\n        }],\n        yAxis: [{\n          type: 'value',\n          splitLine: {\n            show: false\n          },\n          axisLine: {\n            lineStyle: {\n              color: '#90979c'\n            }\n          },\n          axisTick: {\n            show: false\n          },\n          axisLabel: {\n            interval: 0\n          },\n          splitArea: {\n            show: false\n          }\n        }],\n        dataZoom: [{\n          show: true,\n          height: 30,\n          xAxisIndex: [\n            0\n          ],\n          bottom: 30,\n          start: 10,\n          end: 80,\n          handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',\n          handleSize: '110%',\n          handleStyle: {\n            color: '#d3dee5'\n\n          },\n          textStyle: {\n            color: '#fff' },\n          borderColor: '#90979c'\n\n        }, {\n          type: 'inside',\n          show: true,\n          height: 15,\n          start: 1,\n          end: 35\n        }],\n        series: [{\n          name: 'female',\n          type: 'bar',\n          stack: 'total',\n          barMaxWidth: 35,\n          barGap: '10%',\n          itemStyle: {\n            normal: {\n              color: 'rgba(255,144,128,1)',\n              label: {\n                show: true,\n                textStyle: {\n                  color: '#fff'\n                },\n                position: 'insideTop',\n                formatter(p) {\n                  return p.value > 0 ? p.value : ''\n                }\n              }\n            }\n          },\n          data: [\n            709,\n            1917,\n            2455,\n            2610,\n            1719,\n            1433,\n            1544,\n            3285,\n            5208,\n            3372,\n            2484,\n            4078\n          ]\n        },\n\n        {\n          name: 'male',\n          type: 'bar',\n          stack: 'total',\n          itemStyle: {\n            normal: {\n              color: 'rgba(0,191,183,1)',\n              barBorderRadius: 0,\n              label: {\n                show: true,\n                position: 'top',\n                formatter(p) {\n                  return p.value > 0 ? p.value : ''\n                }\n              }\n            }\n          },\n          data: [\n            327,\n            1776,\n            507,\n            1200,\n            800,\n            482,\n            204,\n            1390,\n            1001,\n            951,\n            381,\n            220\n          ]\n        }, {\n          name: 'average',\n          type: 'line',\n          stack: 'total',\n          symbolSize: 10,\n          symbol: 'circle',\n          itemStyle: {\n            normal: {\n              color: 'rgba(252,230,48,1)',\n              barBorderRadius: 0,\n              label: {\n                show: true,\n                position: 'top',\n                formatter(p) {\n                  return p.value > 0 ? p.value : ''\n                }\n              }\n            }\n          },\n          data: [\n            1036,\n            3693,\n            2962,\n            3810,\n            2519,\n            1915,\n            1748,\n            4675,\n            6209,\n            4323,\n            2865,\n            4298\n          ]\n        }\n        ]\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/components/Charts/mixins/resize.js",
    "content": "import { debounce } from '@/utils'\n\nexport default {\n  data() {\n    return {\n      $_sidebarElm: null,\n      $_resizeHandler: null\n    }\n  },\n  mounted() {\n    this.initListener()\n  },\n  activated() {\n    if (!this.$_resizeHandler) {\n      // avoid duplication init\n      this.initListener()\n    }\n\n    // when keep-alive chart activated, auto resize\n    this.resize()\n  },\n  beforeDestroy() {\n    this.destroyListener()\n  },\n  deactivated() {\n    this.destroyListener()\n  },\n  methods: {\n    // use $_ for mixins properties\n    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential\n    $_sidebarResizeHandler(e) {\n      if (e.propertyName === 'width') {\n        this.$_resizeHandler()\n      }\n    },\n    initListener() {\n      this.$_resizeHandler = debounce(() => {\n        this.resize()\n      }, 100)\n      window.addEventListener('resize', this.$_resizeHandler)\n\n      this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]\n      this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)\n    },\n    destroyListener() {\n      window.removeEventListener('resize', this.$_resizeHandler)\n      this.$_resizeHandler = null\n\n      this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)\n    },\n    resize() {\n      const { chart } = this\n      chart && chart.resize()\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/src/components/DndList/index.vue",
    "content": "<template>\n  <div class=\"dndList\">\n    <div :style=\"{width:width1}\" class=\"dndList-list\">\n      <h3>{{ list1Title }}</h3>\n      <draggable :set-data=\"setData\" :list=\"list1\" group=\"article\" class=\"dragArea\">\n        <div v-for=\"element in list1\" :key=\"element.id\" class=\"list-complete-item\">\n          <div class=\"list-complete-item-handle\">\n            {{ element.id }}[{{ element.author }}] {{ element.title }}\n          </div>\n          <div style=\"position:absolute;right:0px;\">\n            <span style=\"float: right ;margin-top: -20px;margin-right:5px;\" @click=\"deleteEle(element)\">\n              <i style=\"color:#ff4949\" class=\"el-icon-delete\" />\n            </span>\n          </div>\n        </div>\n      </draggable>\n    </div>\n    <div :style=\"{width:width2}\" class=\"dndList-list\">\n      <h3>{{ list2Title }}</h3>\n      <draggable :list=\"list2\" group=\"article\" class=\"dragArea\">\n        <div v-for=\"element in list2\" :key=\"element.id\" class=\"list-complete-item\">\n          <div class=\"list-complete-item-handle2\" @click=\"pushEle(element)\">\n            {{ element.id }} [{{ element.author }}] {{ element.title }}\n          </div>\n        </div>\n      </draggable>\n    </div>\n  </div>\n</template>\n\n<script>\nimport draggable from 'vuedraggable'\n\nexport default {\n  name: 'DndList',\n  components: { draggable },\n  props: {\n    list1: {\n      type: Array,\n      default() {\n        return []\n      }\n    },\n    list2: {\n      type: Array,\n      default() {\n        return []\n      }\n    },\n    list1Title: {\n      type: String,\n      default: 'list1'\n    },\n    list2Title: {\n      type: String,\n      default: 'list2'\n    },\n    width1: {\n      type: String,\n      default: '48%'\n    },\n    width2: {\n      type: String,\n      default: '48%'\n    }\n  },\n  methods: {\n    isNotInList1(v) {\n      return this.list1.every(k => v.id !== k.id)\n    },\n    isNotInList2(v) {\n      return this.list2.every(k => v.id !== k.id)\n    },\n    deleteEle(ele) {\n      for (const item of this.list1) {\n        if (item.id === ele.id) {\n          const index = this.list1.indexOf(item)\n          this.list1.splice(index, 1)\n          break\n        }\n      }\n      if (this.isNotInList2(ele)) {\n        this.list2.unshift(ele)\n      }\n    },\n    pushEle(ele) {\n      for (const item of this.list2) {\n        if (item.id === ele.id) {\n          const index = this.list2.indexOf(item)\n          this.list2.splice(index, 1)\n          break\n        }\n      }\n      if (this.isNotInList1(ele)) {\n        this.list1.push(ele)\n      }\n    },\n    setData(dataTransfer) {\n      // to avoid Firefox bug\n      // Detail see : https://github.com/RubaXa/Sortable/issues/1012\n      dataTransfer.setData('Text', '')\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.dndList {\n  background: #fff;\n  padding-bottom: 40px;\n  &:after {\n    content: \"\";\n    display: table;\n    clear: both;\n  }\n  .dndList-list {\n    float: left;\n    padding-bottom: 30px;\n    &:first-of-type {\n      margin-right: 2%;\n    }\n    .dragArea {\n      margin-top: 15px;\n      min-height: 50px;\n      padding-bottom: 30px;\n    }\n  }\n}\n\n.list-complete-item {\n  cursor: pointer;\n  position: relative;\n  font-size: 14px;\n  padding: 5px 12px;\n  margin-top: 4px;\n  border: 1px solid #bfcbd9;\n  transition: all 1s;\n}\n\n.list-complete-item-handle {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  margin-right: 50px;\n}\n\n.list-complete-item-handle2 {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  margin-right: 20px;\n}\n\n.list-complete-item.sortable-chosen {\n  background: #4AB7BD;\n}\n\n.list-complete-item.sortable-ghost {\n  background: #30B08F;\n}\n\n.list-complete-enter,\n.list-complete-leave-active {\n  opacity: 0;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/Hamburger/index.vue",
    "content": "<template>\n  <div style=\"padding: 0 15px;\" @click=\"toggleClick\">\n    <svg\n      :class=\"{'is-active':isActive}\"\n      class=\"hamburger\"\n      viewBox=\"0 0 1024 1024\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"64\"\n      height=\"64\"\n    >\n      <path d=\"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z\" />\n    </svg>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'Hamburger',\n  props: {\n    isActive: {\n      type: Boolean,\n      default: false\n    }\n  },\n  methods: {\n    toggleClick() {\n      this.$emit('toggleClick')\n    }\n  }\n}\n</script>\n\n<style scoped>\n.hamburger {\n  display: inline-block;\n  vertical-align: middle;\n  width: 20px;\n  height: 20px;\n}\n\n.hamburger.is-active {\n  transform: rotate(180deg);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/HeaderSearch/index.vue",
    "content": "<template>\n  <div :class=\"{'show':show}\" class=\"header-search\">\n    <svg-icon class-name=\"search-icon\" icon-class=\"search\" @click.stop=\"click\" />\n    <el-select\n      ref=\"headerSearchSelect\"\n      v-model=\"search\"\n      :remote-method=\"querySearch\"\n      filterable\n      default-first-option\n      remote\n      placeholder=\"Search\"\n      class=\"header-search-select\"\n      @change=\"change\"\n    >\n      <el-option v-for=\"item in options\" :key=\"item.path\" :value=\"item\" :label=\"item.title.join(' > ')\" />\n    </el-select>\n  </div>\n</template>\n\n<script>\n// fuse is a lightweight fuzzy-search module\n// make search results more in line with expectations\nimport Fuse from 'fuse.js'\nimport path from 'path'\nimport i18n from '@/lang'\n\nexport default {\n  name: 'HeaderSearch',\n  data() {\n    return {\n      search: '',\n      options: [],\n      searchPool: [],\n      show: false,\n      fuse: undefined\n    }\n  },\n  computed: {\n    routes() {\n      return this.$store.getters.permission_routes\n    },\n    lang() {\n      return this.$store.getters.language\n    }\n  },\n  watch: {\n    lang() {\n      this.searchPool = this.generateRoutes(this.routes)\n    },\n    routes() {\n      this.searchPool = this.generateRoutes(this.routes)\n    },\n    searchPool(list) {\n      this.initFuse(list)\n    },\n    show(value) {\n      if (value) {\n        document.body.addEventListener('click', this.close)\n      } else {\n        document.body.removeEventListener('click', this.close)\n      }\n    }\n  },\n  mounted() {\n    this.searchPool = this.generateRoutes(this.routes)\n  },\n  methods: {\n    click() {\n      this.show = !this.show\n      if (this.show) {\n        this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()\n      }\n    },\n    close() {\n      this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()\n      this.options = []\n      this.show = false\n    },\n    change(val) {\n      this.$router.push(val.path)\n      this.search = ''\n      this.options = []\n      this.$nextTick(() => {\n        this.show = false\n      })\n    },\n    initFuse(list) {\n      this.fuse = new Fuse(list, {\n        shouldSort: true,\n        threshold: 0.4,\n        location: 0,\n        distance: 100,\n        maxPatternLength: 32,\n        minMatchCharLength: 1,\n        keys: [{\n          name: 'title',\n          weight: 0.7\n        }, {\n          name: 'path',\n          weight: 0.3\n        }]\n      })\n    },\n    // Filter out the routes that can be displayed in the sidebar\n    // And generate the internationalized title\n    generateRoutes(routes, basePath = '/', prefixTitle = []) {\n      let res = []\n\n      for (const router of routes) {\n        // skip hidden router\n        if (router.hidden) { continue }\n\n        const data = {\n          path: path.resolve(basePath, router.path),\n          title: [...prefixTitle]\n        }\n\n        if (router.meta && router.meta.title) {\n          // generate internationalized title\n          const i18ntitle = i18n.t(`route.${router.meta.title}`)\n\n          data.title = [...data.title, i18ntitle]\n\n          if (router.redirect !== 'noredirect') {\n            // only push the routes with title\n            // special case: need to exclude parent router without redirect\n            res.push(data)\n          }\n        }\n\n        // recursive child routes\n        if (router.children) {\n          const tempRoutes = this.generateRoutes(router.children, data.path, data.title)\n          if (tempRoutes.length >= 1) {\n            res = [...res, ...tempRoutes]\n          }\n        }\n      }\n      return res\n    },\n    querySearch(query) {\n      if (query !== '') {\n        this.options = this.fuse.search(query)\n      } else {\n        this.options = []\n      }\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.header-search {\n  font-size: 0 !important;\n\n  .search-icon {\n    cursor: pointer;\n    font-size: 18px;\n    vertical-align: middle;\n  }\n\n  .header-search-select {\n    font-size: 18px;\n    transition: width 0.2s;\n    width: 0;\n    overflow: hidden;\n    background: transparent;\n    border-radius: 0;\n    display: inline-block;\n    vertical-align: middle;\n\n    /deep/ .el-input__inner {\n      border-radius: 0;\n      border: 0;\n      padding-left: 0;\n      padding-right: 0;\n      box-shadow: none !important;\n      border-bottom: 1px solid #d9d9d9;\n      vertical-align: middle;\n    }\n  }\n\n  &.show {\n    .header-search-select {\n      width: 210px;\n      margin-left: 10px;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/Kanban/index.vue",
    "content": "<template>\n  <div class=\"board-column\">\n    <div class=\"board-column-header\">\n      {{ headerText }}\n    </div>\n    <draggable\n      :list=\"list\"\n      v-bind=\"$attrs\"\n      class=\"board-column-content\"\n      :set-data=\"setData\"\n    >\n      <div v-for=\"element in list\" :key=\"element.id\" class=\"board-item\">\n        {{ element.name }} {{ element.id }}\n      </div>\n    </draggable>\n  </div>\n</template>\n\n<script>\nimport draggable from 'vuedraggable'\n\nexport default {\n  name: 'DragKanbanDemo',\n  components: {\n    draggable\n  },\n  props: {\n    headerText: {\n      type: String,\n      default: 'Header'\n    },\n    options: {\n      type: Object,\n      default() {\n        return {}\n      }\n    },\n    list: {\n      type: Array,\n      default() {\n        return []\n      }\n    }\n  },\n  methods: {\n    setData(dataTransfer) {\n      // to avoid Firefox bug\n      // Detail see : https://github.com/RubaXa/Sortable/issues/1012\n      dataTransfer.setData('Text', '')\n    }\n  }\n}\n</script>\n<style lang=\"scss\" scoped>\n.board-column {\n  min-width: 300px;\n  min-height: 100px;\n  height: auto;\n  overflow: hidden;\n  background: #f0f0f0;\n  border-radius: 3px;\n\n  .board-column-header {\n    height: 50px;\n    line-height: 50px;\n    overflow: hidden;\n    padding: 0 20px;\n    text-align: center;\n    background: #333;\n    color: #fff;\n    border-radius: 3px 3px 0 0;\n  }\n\n  .board-column-content {\n    height: auto;\n    overflow: hidden;\n    border: 10px solid transparent;\n    min-height: 60px;\n    display: flex;\n    justify-content: flex-start;\n    flex-direction: column;\n    align-items: center;\n\n    .board-item {\n      cursor: pointer;\n      width: 100%;\n      height: 64px;\n      margin: 5px 0;\n      background-color: #fff;\n      text-align: left;\n      line-height: 54px;\n      padding: 5px 10px;\n      box-sizing: border-box;\n      box-shadow: 0px 1px 3px 0 rgba(0, 0, 0, 0.2);\n    }\n  }\n}\n</style>\n\n"
  },
  {
    "path": "frontend/src/components/LangSelect/index.vue",
    "content": "<template>\n  <el-dropdown trigger=\"click\" class=\"international\" @command=\"handleSetLanguage\">\n    <div>\n      <svg-icon class-name=\"international-icon\" icon-class=\"international\" />\n    </div>\n    <el-dropdown-menu slot=\"dropdown\">\n      <el-dropdown-item :disabled=\"language==='zh'\" command=\"zh\">\n        中文\n      </el-dropdown-item>\n      <el-dropdown-item :disabled=\"language==='en'\" command=\"en\">\n        English\n      </el-dropdown-item>\n    </el-dropdown-menu>\n  </el-dropdown>\n</template>\n\n<script>\nexport default {\n  computed: {\n    language() {\n      return this.$store.getters.language\n    }\n  },\n  methods: {\n    handleSetLanguage(lang) {\n      this.$i18n.locale = lang\n      this.$store.dispatch('app/setLanguage', lang)\n      this.$message({\n        message: 'Switch Language Success',\n        type: 'success'\n      })\n    }\n  }\n}\n</script>\n\n"
  },
  {
    "path": "frontend/src/components/MDinput/index.vue",
    "content": "<template>\n  <div :class=\"computedClasses\" class=\"material-input__component\">\n    <div :class=\"{iconClass:icon}\">\n      <i v-if=\"icon\" :class=\"['el-icon-' + icon]\" class=\"el-input__icon material-input__icon\" />\n      <input\n        v-if=\"type === 'email'\"\n        v-model=\"currentValue\"\n        :name=\"name\"\n        :placeholder=\"fillPlaceHolder\"\n        :readonly=\"readonly\"\n        :disabled=\"disabled\"\n        :autocomplete=\"autoComplete\"\n        :required=\"required\"\n        type=\"email\"\n        class=\"material-input\"\n        @focus=\"handleMdFocus\"\n        @blur=\"handleMdBlur\"\n        @input=\"handleModelInput\"\n      >\n      <input\n        v-if=\"type === 'url'\"\n        v-model=\"currentValue\"\n        :name=\"name\"\n        :placeholder=\"fillPlaceHolder\"\n        :readonly=\"readonly\"\n        :disabled=\"disabled\"\n        :autocomplete=\"autoComplete\"\n        :required=\"required\"\n        type=\"url\"\n        class=\"material-input\"\n        @focus=\"handleMdFocus\"\n        @blur=\"handleMdBlur\"\n        @input=\"handleModelInput\"\n      >\n      <input\n        v-if=\"type === 'number'\"\n        v-model=\"currentValue\"\n        :name=\"name\"\n        :placeholder=\"fillPlaceHolder\"\n        :step=\"step\"\n        :readonly=\"readonly\"\n        :disabled=\"disabled\"\n        :autocomplete=\"autoComplete\"\n        :max=\"max\"\n        :min=\"min\"\n        :minlength=\"minlength\"\n        :maxlength=\"maxlength\"\n        :required=\"required\"\n        type=\"number\"\n        class=\"material-input\"\n        @focus=\"handleMdFocus\"\n        @blur=\"handleMdBlur\"\n        @input=\"handleModelInput\"\n      >\n      <input\n        v-if=\"type === 'password'\"\n        v-model=\"currentValue\"\n        :name=\"name\"\n        :placeholder=\"fillPlaceHolder\"\n        :readonly=\"readonly\"\n        :disabled=\"disabled\"\n        :autocomplete=\"autoComplete\"\n        :max=\"max\"\n        :min=\"min\"\n        :required=\"required\"\n        type=\"password\"\n        class=\"material-input\"\n        @focus=\"handleMdFocus\"\n        @blur=\"handleMdBlur\"\n        @input=\"handleModelInput\"\n      >\n      <input\n        v-if=\"type === 'tel'\"\n        v-model=\"currentValue\"\n        :name=\"name\"\n        :placeholder=\"fillPlaceHolder\"\n        :readonly=\"readonly\"\n        :disabled=\"disabled\"\n        :autocomplete=\"autoComplete\"\n        :required=\"required\"\n        type=\"tel\"\n        class=\"material-input\"\n        @focus=\"handleMdFocus\"\n        @blur=\"handleMdBlur\"\n        @input=\"handleModelInput\"\n      >\n      <input\n        v-if=\"type === 'text'\"\n        v-model=\"currentValue\"\n        :name=\"name\"\n        :placeholder=\"fillPlaceHolder\"\n        :readonly=\"readonly\"\n        :disabled=\"disabled\"\n        :autocomplete=\"autoComplete\"\n        :minlength=\"minlength\"\n        :maxlength=\"maxlength\"\n        :required=\"required\"\n        type=\"text\"\n        class=\"material-input\"\n        @focus=\"handleMdFocus\"\n        @blur=\"handleMdBlur\"\n        @input=\"handleModelInput\"\n      >\n      <span class=\"material-input-bar\" />\n      <label class=\"material-label\">\n        <slot />\n      </label>\n    </div>\n  </div>\n</template>\n\n<script>\n// source:https://github.com/wemake-services/vue-material-input/blob/master/src/components/MaterialInput.vue\n\nexport default {\n  name: 'MdInput',\n  props: {\n    /* eslint-disable */\n    icon: String,\n    name: String,\n    type: {\n      type: String,\n      default: 'text'\n    },\n    value: [String, Number],\n    placeholder: String,\n    readonly: Boolean,\n    disabled: Boolean,\n    min: String,\n    max: String,\n    step: String,\n    minlength: Number,\n    maxlength: Number,\n    required: {\n      type: Boolean,\n      default: true\n    },\n    autoComplete: {\n      type: String,\n      default: 'off'\n    },\n    validateEvent: {\n      type: Boolean,\n      default: true\n    }\n  },\n  data() {\n    return {\n      currentValue: this.value,\n      focus: false,\n      fillPlaceHolder: null\n    }\n  },\n  computed: {\n    computedClasses() {\n      return {\n        'material--active': this.focus,\n        'material--disabled': this.disabled,\n        'material--raised': Boolean(this.focus || this.currentValue) // has value\n      }\n    }\n  },\n  watch: {\n    value(newValue) {\n      this.currentValue = newValue\n    }\n  },\n  methods: {\n    handleModelInput(event) {\n      const value = event.target.value\n      this.$emit('input', value)\n      if (this.$parent.$options.componentName === 'ElFormItem') {\n        if (this.validateEvent) {\n          this.$parent.$emit('el.form.change', [value])\n        }\n      }\n      this.$emit('change', value)\n    },\n    handleMdFocus(event) {\n      this.focus = true\n      this.$emit('focus', event)\n      if (this.placeholder && this.placeholder !== '') {\n        this.fillPlaceHolder = this.placeholder\n      }\n    },\n    handleMdBlur(event) {\n      this.focus = false\n      this.$emit('blur', event)\n      this.fillPlaceHolder = null\n      if (this.$parent.$options.componentName === 'ElFormItem') {\n        if (this.validateEvent) {\n          this.$parent.$emit('el.form.blur', [this.currentValue])\n        }\n      }\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n  // Fonts:\n  $font-size-base: 16px;\n  $font-size-small: 18px;\n  $font-size-smallest: 12px;\n  $font-weight-normal: normal;\n  $font-weight-bold: bold;\n  $apixel: 1px;\n  // Utils\n  $spacer: 12px;\n  $transition: 0.2s ease all;\n  $index: 0px;\n  $index-has-icon: 30px;\n  // Theme:\n  $color-white: white;\n  $color-grey: #9E9E9E;\n  $color-grey-light: #E0E0E0;\n  $color-blue: #2196F3;\n  $color-red: #F44336;\n  $color-black: black;\n  // Base clases:\n  %base-bar-pseudo {\n    content: '';\n    height: 1px;\n    width: 0;\n    bottom: 0;\n    position: absolute;\n    transition: $transition;\n  }\n\n  // Mixins:\n  @mixin slided-top() {\n    top: - ($font-size-base + $spacer);\n    left: 0;\n    font-size: $font-size-base;\n    font-weight: $font-weight-bold;\n  }\n\n  // Component:\n  .material-input__component {\n    margin-top: 36px;\n    position: relative;\n    * {\n      box-sizing: border-box;\n    }\n    .iconClass {\n      .material-input__icon {\n        position: absolute;\n        left: 0;\n        line-height: $font-size-base;\n        color: $color-blue;\n        top: $spacer;\n        width: $index-has-icon;\n        height: $font-size-base;\n        font-size: $font-size-base;\n        font-weight: $font-weight-normal;\n        pointer-events: none;\n      }\n      .material-label {\n        left: $index-has-icon;\n      }\n      .material-input {\n        text-indent: $index-has-icon;\n      }\n    }\n    .material-input {\n      font-size: $font-size-base;\n      padding: $spacer $spacer $spacer - $apixel * 10 $spacer / 2;\n      display: block;\n      width: 100%;\n      border: none;\n      line-height: 1;\n      border-radius: 0;\n      &:focus {\n        outline: none;\n        border: none;\n        border-bottom: 1px solid transparent; // fixes the height issue\n      }\n    }\n    .material-label {\n      font-weight: $font-weight-normal;\n      position: absolute;\n      pointer-events: none;\n      left: $index;\n      top: 0;\n      transition: $transition;\n      font-size: $font-size-small;\n    }\n    .material-input-bar {\n      position: relative;\n      display: block;\n      width: 100%;\n      &:before {\n        @extend %base-bar-pseudo;\n        left: 50%;\n      }\n      &:after {\n        @extend %base-bar-pseudo;\n        right: 50%;\n      }\n    }\n    // Disabled state:\n    &.material--disabled {\n      .material-input {\n        border-bottom-style: dashed;\n      }\n    }\n    // Raised state:\n    &.material--raised {\n      .material-label {\n        @include slided-top();\n      }\n    }\n    // Active state:\n    &.material--active {\n      .material-input-bar {\n        &:before,\n        &:after {\n          width: 50%;\n        }\n      }\n    }\n  }\n\n  .material-input__component {\n    background: $color-white;\n    .material-input {\n      background: none;\n      color: $color-black;\n      text-indent: $index;\n      border-bottom: 1px solid $color-grey-light;\n    }\n    .material-label {\n      color: $color-grey;\n    }\n    .material-input-bar {\n      &:before,\n      &:after {\n        background: $color-blue;\n      }\n    }\n    // Active state:\n    &.material--active {\n      .material-label {\n        color: $color-blue;\n      }\n    }\n    // Errors:\n    &.material--has-errors {\n      &.material--active .material-label {\n        color: $color-red;\n      }\n      .material-input-bar {\n        &:before,\n        &:after {\n          background: transparent;\n        }\n      }\n    }\n  }\n</style>\n"
  },
  {
    "path": "frontend/src/components/Pagination/index.vue",
    "content": "<template>\n  <div :class=\"{'hidden':hidden}\" class=\"pagination-container\">\n    <el-pagination\n      :background=\"background\"\n      :current-page.sync=\"currentPage\"\n      :page-size.sync=\"pageSize\"\n      :layout=\"layout\"\n      :page-sizes=\"pageSizes\"\n      :total=\"total\"\n      v-bind=\"$attrs\"\n      @size-change=\"handleSizeChange\"\n      @current-change=\"handleCurrentChange\"\n    />\n  </div>\n</template>\n\n<script>\nimport { scrollTo } from '@/utils/scroll-to'\n\nexport default {\n  name: 'Pagination',\n  props: {\n    total: {\n      required: true,\n      type: Number\n    },\n    page: {\n      type: Number,\n      default: 1\n    },\n    limit: {\n      type: Number,\n      default: 20\n    },\n    pageSizes: {\n      type: Array,\n      default() {\n        return [10, 20, 30, 50]\n      }\n    },\n    layout: {\n      type: String,\n      default: 'total, sizes, prev, pager, next, jumper'\n    },\n    background: {\n      type: Boolean,\n      default: true\n    },\n    autoScroll: {\n      type: Boolean,\n      default: true\n    },\n    hidden: {\n      type: Boolean,\n      default: false\n    }\n  },\n  computed: {\n    currentPage: {\n      get() {\n        return this.page\n      },\n      set(val) {\n        this.$emit('update:page', val)\n      }\n    },\n    pageSize: {\n      get() {\n        return this.limit\n      },\n      set(val) {\n        this.$emit('update:limit', val)\n      }\n    }\n  },\n  methods: {\n    handleSizeChange(val) {\n      this.$emit('pagination', { page: this.currentPage, limit: val })\n      if (this.autoScroll) {\n        scrollTo(0, 800)\n      }\n    },\n    handleCurrentChange(val) {\n      this.$emit('pagination', { page: val, limit: this.pageSize })\n      if (this.autoScroll) {\n        scrollTo(0, 800)\n      }\n    }\n  }\n}\n</script>\n\n<style scoped>\n.pagination-container {\n  background: #fff;\n  padding: 32px 16px;\n}\n.pagination-container.hidden {\n  display: none;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/PanThumb/index.vue",
    "content": "<template>\n  <div :style=\"{zIndex:zIndex,height:height,width:width}\" class=\"pan-item\">\n    <div class=\"pan-info\">\n      <div class=\"pan-info-roles-container\">\n        <slot />\n      </div>\n    </div>\n    <!-- eslint-disable-next-line -->\n    <div :style=\"{backgroundImage: `url(${image})`}\" class=\"pan-thumb\"></div>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'PanThumb',\n  props: {\n    image: {\n      type: String,\n      required: true\n    },\n    zIndex: {\n      type: Number,\n      default: 1\n    },\n    width: {\n      type: String,\n      default: '150px'\n    },\n    height: {\n      type: String,\n      default: '150px'\n    }\n  }\n}\n</script>\n\n<style scoped>\n.pan-item {\n  width: 200px;\n  height: 200px;\n  border-radius: 50%;\n  display: inline-block;\n  position: relative;\n  cursor: default;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n}\n\n.pan-info-roles-container {\n  padding: 20px;\n  text-align: center;\n}\n\n.pan-thumb {\n  width: 100%;\n  height: 100%;\n  background-position: center center;\n  background-size: cover;\n  border-radius: 50%;\n  overflow: hidden;\n  position: absolute;\n  transform-origin: 95% 40%;\n  transition: all 0.3s ease-in-out;\n}\n\n/* .pan-thumb:after {\n  content: '';\n  width: 8px;\n  height: 8px;\n  position: absolute;\n  border-radius: 50%;\n  top: 40%;\n  left: 95%;\n  margin: -4px 0 0 -4px;\n  background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);\n  box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);\n} */\n\n.pan-info {\n  position: absolute;\n  width: inherit;\n  height: inherit;\n  border-radius: 50%;\n  overflow: hidden;\n  box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);\n}\n\n.pan-info h3 {\n  color: #fff;\n  text-transform: uppercase;\n  position: relative;\n  letter-spacing: 2px;\n  font-size: 18px;\n  margin: 0 60px;\n  padding: 22px 0 0 0;\n  height: 85px;\n  font-family: 'Open Sans', Arial, sans-serif;\n  text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);\n}\n\n.pan-info p {\n  color: #fff;\n  padding: 10px 5px;\n  font-style: italic;\n  margin: 0 30px;\n  font-size: 12px;\n  border-top: 1px solid rgba(255, 255, 255, 0.5);\n}\n\n.pan-info p a {\n  display: block;\n  color: #333;\n  width: 80px;\n  height: 80px;\n  background: rgba(255, 255, 255, 0.3);\n  border-radius: 50%;\n  color: #fff;\n  font-style: normal;\n  font-weight: 700;\n  text-transform: uppercase;\n  font-size: 9px;\n  letter-spacing: 1px;\n  padding-top: 24px;\n  margin: 7px auto 0;\n  font-family: 'Open Sans', Arial, sans-serif;\n  opacity: 0;\n  transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;\n  transform: translateX(60px) rotate(90deg);\n}\n\n.pan-info p a:hover {\n  background: rgba(255, 255, 255, 0.5);\n}\n\n.pan-item:hover .pan-thumb {\n  transform: rotate(-110deg);\n}\n\n.pan-item:hover .pan-info p a {\n  opacity: 1;\n  transform: translateX(0px) rotate(0deg);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/RightPanel/index.vue",
    "content": "<template>\n  <div ref=\"rightPanel\" :class=\"{show:show}\" class=\"rightPanel-container\">\n    <div class=\"rightPanel-background\" />\n    <div class=\"rightPanel\">\n      <div class=\"handle-button\" :style=\"{'top':buttonTop+'px','background-color':theme}\" @click=\"show=!show\">\n        <i :class=\"show?'el-icon-close':'el-icon-setting'\" />\n      </div>\n      <div class=\"rightPanel-items\">\n        <slot />\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { addClass, removeClass } from '@/utils'\n\nexport default {\n  name: 'RightPanel',\n  props: {\n    clickNotClose: {\n      default: false,\n      type: Boolean\n    },\n    buttonTop: {\n      default: 250,\n      type: Number\n    }\n  },\n  data() {\n    return {\n      show: false\n    }\n  },\n  computed: {\n    theme() {\n      return this.$store.state.settings.theme\n    }\n  },\n  watch: {\n    show(value) {\n      if (value && !this.clickNotClose) {\n        this.addEventClick()\n      }\n      if (value) {\n        addClass(document.body, 'showRightPanel')\n      } else {\n        removeClass(document.body, 'showRightPanel')\n      }\n    }\n  },\n  mounted() {\n    this.insertToBody()\n  },\n  beforeDestroy() {\n    const elx = this.$refs.rightPanel\n    elx.remove()\n  },\n  methods: {\n    addEventClick() {\n      window.addEventListener('click', this.closeSidebar)\n    },\n    closeSidebar(evt) {\n      const parent = evt.target.closest('.rightPanel')\n      if (!parent) {\n        this.show = false\n        window.removeEventListener('click', this.closeSidebar)\n      }\n    },\n    insertToBody() {\n      const elx = this.$refs.rightPanel\n      const body = document.querySelector('body')\n      body.insertBefore(elx, body.firstChild)\n    }\n  }\n}\n</script>\n\n<style>\n.showRightPanel {\n  overflow: hidden;\n  position: relative;\n  width: calc(100% - 15px);\n}\n</style>\n\n<style lang=\"scss\" scoped>\n.rightPanel-background {\n  position: fixed;\n  top: 0;\n  left: 0;\n  opacity: 0;\n  transition: opacity .3s cubic-bezier(.7, .3, .1, 1);\n  background: rgba(0, 0, 0, .2);\n  z-index: -1;\n}\n\n.rightPanel {\n  width: 100%;\n  max-width: 260px;\n  height: 100vh;\n  position: fixed;\n  top: 0;\n  right: 0;\n  box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05);\n  transition: all .25s cubic-bezier(.7, .3, .1, 1);\n  transform: translate(100%);\n  z-index: 30000;\n  background-image: url('../../assets/panel-bg.png');\n  background-size:cover;\n  //background-position: center;\n}\n\n.show {\n  transition: all .3s cubic-bezier(.7, .3, .1, 1);\n\n  .rightPanel-background {\n    z-index: 20000;\n    opacity: 1;\n    width: 100%;\n    height: 100%;\n  }\n\n  .rightPanel {\n    transform: translate(0);\n  }\n}\n\n.handle-button {\n  width: 48px;\n  height: 48px;\n  position: absolute;\n  left: -48px;\n  text-align: center;\n  font-size: 24px;\n  border-radius: 6px 0 0 6px !important;\n  z-index: 0;\n  pointer-events: auto;\n  cursor: pointer;\n  color: #fff;\n  line-height: 48px;\n  i {\n    font-size: 24px;\n    line-height: 48px;\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/Screenfull/index.vue",
    "content": "<template>\n  <div>\n    <svg-icon :icon-class=\"isFullscreen?'exit-fullscreen':'fullscreen'\" @click=\"click\" />\n  </div>\n</template>\n\n<script>\nimport screenfull from 'screenfull'\n\nexport default {\n  name: 'Screenfull',\n  data() {\n    return {\n      isFullscreen: false\n    }\n  },\n  mounted() {\n    this.init()\n  },\n  beforeDestroy() {\n    this.destroy()\n  },\n  methods: {\n    click() {\n      if (!screenfull.enabled) {\n        this.$message({\n          message: 'you browser can not work',\n          type: 'warning'\n        })\n        return false\n      }\n      screenfull.toggle()\n    },\n    change() {\n      this.isFullscreen = screenfull.isFullscreen\n    },\n    init() {\n      if (screenfull.enabled) {\n        screenfull.on('change', this.change)\n      }\n    },\n    destroy() {\n      if (screenfull.enabled) {\n        screenfull.off('change', this.change)\n      }\n    }\n  }\n}\n</script>\n\n<style scoped>\n.screenfull-svg {\n  display: inline-block;\n  cursor: pointer;\n  fill: #5a5e66;;\n  width: 20px;\n  height: 20px;\n  vertical-align: 10px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/Share/DropdownMenu.vue",
    "content": "<template>\n  <div :class=\"{active:isActive}\" class=\"share-dropdown-menu\">\n    <div class=\"share-dropdown-menu-wrapper\">\n      <span class=\"share-dropdown-menu-title\" @click.self=\"clickTitle\">{{ title }}</span>\n      <div v-for=\"(item,index) of items\" :key=\"index\" class=\"share-dropdown-menu-item\">\n        <a v-if=\"item.href\" :href=\"item.href\" target=\"_blank\">{{ item.title }}</a>\n        <span v-else>{{ item.title }}</span>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nexport default {\n  props: {\n    items: {\n      type: Array,\n      default: function() {\n        return []\n      }\n    },\n    title: {\n      type: String,\n      default: 'vue'\n    }\n  },\n  data() {\n    return {\n      isActive: false\n    }\n  },\n  methods: {\n    clickTitle() {\n      this.isActive = !this.isActive\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" >\n$n: 9; //和items.length 相同\n$t: .1s;\n.share-dropdown-menu {\n  width: 250px;\n  position: relative;\n  z-index: 1;\n  height: auto!important;\n  &-title {\n    width: 100%;\n    display: block;\n    cursor: pointer;\n    background: black;\n    color: white;\n    height: 60px;\n    line-height: 60px;\n    font-size: 20px;\n    text-align: center;\n    z-index: 2;\n    transform: translate3d(0,0,0);\n  }\n  &-wrapper {\n    position: relative;\n  }\n  &-item {\n    text-align: center;\n    position: absolute;\n    width: 100%;\n    background: #e0e0e0;\n    color: #000;\n    line-height: 60px;\n    height: 60px;\n    cursor: pointer;\n    font-size: 18px;\n    overflow: hidden;\n    opacity: 1;\n    transition: transform 0.28s ease;\n    &:hover {\n      background: black;\n      color: white;\n    }\n    @for $i from 1 through $n {\n      &:nth-of-type(#{$i}) {\n        z-index: -1;\n        transition-delay: $i*$t;\n        transform: translate3d(0, -60px, 0);\n      }\n    }\n  }\n  &.active {\n    .share-dropdown-menu-wrapper {\n      z-index: 1;\n    }\n    .share-dropdown-menu-item {\n      @for $i from 1 through $n {\n        &:nth-of-type(#{$i}) {\n          transition-delay: ($n - $i)*$t;\n          transform: translate3d(0, ($i - 1)*60px, 0);\n        }\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/Sticky/index.vue",
    "content": "<template>\n  <div :style=\"{height:height+'px',zIndex:zIndex}\">\n    <div\n      :class=\"className\"\n      :style=\"{top:(isSticky ? stickyTop +'px' : ''),zIndex:zIndex,position:position,width:width,height:height+'px'}\"\n    >\n      <slot>\n        <div>sticky</div>\n      </slot>\n    </div>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'Sticky',\n  props: {\n    stickyTop: {\n      type: Number,\n      default: 0\n    },\n    zIndex: {\n      type: Number,\n      default: 1\n    },\n    className: {\n      type: String,\n      default: ''\n    }\n  },\n  data() {\n    return {\n      active: false,\n      position: '',\n      width: undefined,\n      height: undefined,\n      isSticky: false\n    }\n  },\n  mounted() {\n    this.height = this.$el.getBoundingClientRect().height\n    window.addEventListener('scroll', this.handleScroll)\n    window.addEventListener('resize', this.handleResize)\n  },\n  activated() {\n    this.handleScroll()\n  },\n  destroyed() {\n    window.removeEventListener('scroll', this.handleScroll)\n    window.removeEventListener('resize', this.handleResize)\n  },\n  methods: {\n    sticky() {\n      if (this.active) {\n        return\n      }\n      this.position = 'fixed'\n      this.active = true\n      this.width = this.width + 'px'\n      this.isSticky = true\n    },\n    handleReset() {\n      if (!this.active) {\n        return\n      }\n      this.reset()\n    },\n    reset() {\n      this.position = ''\n      this.width = 'auto'\n      this.active = false\n      this.isSticky = false\n    },\n    handleScroll() {\n      const width = this.$el.getBoundingClientRect().width\n      this.width = width || 'auto'\n      const offsetTop = this.$el.getBoundingClientRect().top\n      if (offsetTop < this.stickyTop) {\n        this.sticky()\n        return\n      }\n      this.handleReset()\n    },\n    handleResize() {\n      if (this.isSticky) {\n        this.width = this.$el.getBoundingClientRect().width + 'px'\n      }\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/components/SvgIcon/index.vue",
    "content": "<template>\n  <div v-if=\"isExternal\" :style=\"styleExternalIcon\" class=\"svg-external-icon svg-icon\" v-on=\"$listeners\" />\n  <svg v-else :class=\"svgClass\" aria-hidden=\"true\" v-on=\"$listeners\">\n    <use :xlink:href=\"iconName\" />\n  </svg>\n</template>\n\n<script>\n// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage\nimport { isExternal } from '@/utils/validate'\n\nexport default {\n  name: 'SvgIcon',\n  props: {\n    iconClass: {\n      type: String,\n      required: true\n    },\n    className: {\n      type: String,\n      default: ''\n    }\n  },\n  computed: {\n    isExternal() {\n      return isExternal(this.iconClass)\n    },\n    iconName() {\n      return `#icon-${this.iconClass}`\n    },\n    svgClass() {\n      if (this.className) {\n        return 'svg-icon ' + this.className\n      } else {\n        return 'svg-icon'\n      }\n    },\n    styleExternalIcon() {\n      return {\n        mask: `url(${this.iconClass}) no-repeat 50% 50%`,\n        '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`\n      }\n    }\n  }\n}\n</script>\n\n<style scoped>\n.svg-icon {\n  width: 1em;\n  height: 1em;\n  vertical-align: -0.15em;\n  fill: currentColor;\n  overflow: hidden;\n}\n\n.svg-external-icon {\n  background-color: currentColor;\n  mask-size: cover!important;\n  display: inline-block;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/TextHoverEffect/Mallki.vue",
    "content": "<template>\n  <a :class=\"className\" class=\"link--mallki\" href=\"#\">\n    {{ text }}\n    <span :data-letters=\"text\" />\n    <span :data-letters=\"text\" />\n  </a>\n</template>\n\n<script>\nexport default {\n  props: {\n    className: {\n      type: String,\n      default: ''\n    },\n    text: {\n      type: String,\n      default: 'vue-element-admin'\n    }\n  }\n}\n</script>\n\n<style>\n/* Mallki */\n\n.link--mallki {\n  font-weight: 800;\n  color: #4dd9d5;\n  font-family: 'Dosis', sans-serif;\n  -webkit-transition: color 0.5s 0.25s;\n  transition: color 0.5s 0.25s;\n  overflow: hidden;\n  position: relative;\n  display: inline-block;\n  line-height: 1;\n  outline: none;\n  text-decoration: none;\n}\n\n.link--mallki:hover {\n  -webkit-transition: none;\n  transition: none;\n  color: transparent;\n}\n\n.link--mallki::before {\n  content: '';\n  width: 100%;\n  height: 6px;\n  margin: -3px 0 0 0;\n  background: #3888fa;\n  position: absolute;\n  left: 0;\n  top: 50%;\n  -webkit-transform: translate3d(-100%, 0, 0);\n  transform: translate3d(-100%, 0, 0);\n  -webkit-transition: -webkit-transform 0.4s;\n  transition: transform 0.4s;\n  -webkit-transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);\n  transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);\n}\n\n.link--mallki:hover::before {\n  -webkit-transform: translate3d(100%, 0, 0);\n  transform: translate3d(100%, 0, 0);\n}\n\n.link--mallki span {\n  position: absolute;\n  height: 50%;\n  width: 100%;\n  left: 0;\n  top: 0;\n  overflow: hidden;\n}\n\n.link--mallki span::before {\n  content: attr(data-letters);\n  color: red;\n  position: absolute;\n  left: 0;\n  width: 100%;\n  color: #3888fa;\n  -webkit-transition: -webkit-transform 0.5s;\n  transition: transform 0.5s;\n}\n\n.link--mallki span:nth-child(2) {\n  top: 50%;\n}\n\n.link--mallki span:first-child::before {\n  top: 0;\n  -webkit-transform: translate3d(0, 100%, 0);\n  transform: translate3d(0, 100%, 0);\n}\n\n.link--mallki span:nth-child(2)::before {\n  bottom: 0;\n  -webkit-transform: translate3d(0, -100%, 0);\n  transform: translate3d(0, -100%, 0);\n}\n\n.link--mallki:hover span::before {\n  -webkit-transition-delay: 0.3s;\n  transition-delay: 0.3s;\n  -webkit-transform: translate3d(0, 0, 0);\n  transform: translate3d(0, 0, 0);\n  -webkit-transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);\n  transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/ThemePicker/index.vue",
    "content": "<template>\n  <el-color-picker\n    v-model=\"theme\"\n    :predefine=\"['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]\"\n    class=\"theme-picker\"\n    popper-class=\"theme-picker-dropdown\"\n  />\n</template>\n\n<script>\nconst version = require('element-ui/package.json').version // element-ui version from node_modules\nconst ORIGINAL_THEME = '#409EFF' // default color\n\nexport default {\n  data() {\n    return {\n      chalk: '', // content of theme-chalk css\n      theme: ''\n    }\n  },\n  computed: {\n    defaultTheme() {\n      return this.$store.state.settings.theme\n    }\n  },\n  watch: {\n    defaultTheme: {\n      handler: function(val, oldVal) {\n        this.theme = val\n      },\n      immediate: true\n    },\n    async theme(val) {\n      const oldVal = this.chalk ? this.theme : ORIGINAL_THEME\n      if (typeof val !== 'string') return\n      const themeCluster = this.getThemeCluster(val.replace('#', ''))\n      const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))\n      console.log(themeCluster, originalCluster)\n\n      const $message = this.$message({\n        message: '  Compiling the theme',\n        customClass: 'theme-message',\n        type: 'success',\n        duration: 0,\n        iconClass: 'el-icon-loading'\n      })\n\n      const getHandler = (variable, id) => {\n        return () => {\n          const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))\n          const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)\n\n          let styleTag = document.getElementById(id)\n          if (!styleTag) {\n            styleTag = document.createElement('style')\n            styleTag.setAttribute('id', id)\n            document.head.appendChild(styleTag)\n          }\n          styleTag.innerText = newStyle\n        }\n      }\n\n      if (!this.chalk) {\n        const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`\n        await this.getCSSString(url, 'chalk')\n      }\n\n      const chalkHandler = getHandler('chalk', 'chalk-style')\n\n      chalkHandler()\n\n      const styles = [].slice.call(document.querySelectorAll('style'))\n        .filter(style => {\n          const text = style.innerText\n          return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)\n        })\n      styles.forEach(style => {\n        const { innerText } = style\n        if (typeof innerText !== 'string') return\n        style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)\n      })\n\n      this.$emit('change', val)\n\n      $message.close()\n    }\n  },\n\n  methods: {\n    updateStyle(style, oldCluster, newCluster) {\n      let newStyle = style\n      oldCluster.forEach((color, index) => {\n        newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])\n      })\n      return newStyle\n    },\n\n    getCSSString(url, variable) {\n      return new Promise(resolve => {\n        const xhr = new XMLHttpRequest()\n        xhr.onreadystatechange = () => {\n          if (xhr.readyState === 4 && xhr.status === 200) {\n            this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')\n            resolve()\n          }\n        }\n        xhr.open('GET', url)\n        xhr.send()\n      })\n    },\n\n    getThemeCluster(theme) {\n      const tintColor = (color, tint) => {\n        let red = parseInt(color.slice(0, 2), 16)\n        let green = parseInt(color.slice(2, 4), 16)\n        let blue = parseInt(color.slice(4, 6), 16)\n\n        if (tint === 0) { // when primary color is in its rgb space\n          return [red, green, blue].join(',')\n        } else {\n          red += Math.round(tint * (255 - red))\n          green += Math.round(tint * (255 - green))\n          blue += Math.round(tint * (255 - blue))\n\n          red = red.toString(16)\n          green = green.toString(16)\n          blue = blue.toString(16)\n\n          return `#${red}${green}${blue}`\n        }\n      }\n\n      const shadeColor = (color, shade) => {\n        let red = parseInt(color.slice(0, 2), 16)\n        let green = parseInt(color.slice(2, 4), 16)\n        let blue = parseInt(color.slice(4, 6), 16)\n\n        red = Math.round((1 - shade) * red)\n        green = Math.round((1 - shade) * green)\n        blue = Math.round((1 - shade) * blue)\n\n        red = red.toString(16)\n        green = green.toString(16)\n        blue = blue.toString(16)\n\n        return `#${red}${green}${blue}`\n      }\n\n      const clusters = [theme]\n      for (let i = 0; i <= 9; i++) {\n        clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))\n      }\n      clusters.push(shadeColor(theme, 0.1))\n      return clusters\n    }\n  }\n}\n</script>\n\n<style>\n.theme-message,\n.theme-picker-dropdown {\n  z-index: 99999 !important;\n}\n\n.theme-picker .el-color-picker__trigger {\n  height: 26px !important;\n  width: 26px !important;\n  padding: 2px;\n}\n\n.theme-picker-dropdown .el-color-dropdown__link-btn {\n  display: none;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/TreeSelect/index.vue",
    "content": "<template>\n  <el-select\n    :placeholder=\"props.placeholder\"\n    :value=\"valueTitle\"\n    :clearable=\"clearable\"\n    @clear=\"clearHandle\"\n  >\n    <el-option :value=\"valueTitle\" :label=\"valueTitle\" class=\"options\">\n      <el-tree\n        id=\"tree-option\"\n        ref=\"selectTree\"\n        :accordion=\"accordion\"\n        :data=\"options\"\n        :props=\"props\"\n        :node-key=\"props.value\"\n        :default-expanded-keys=\"defaultExpandedKey\"\n        @node-click=\"handleNodeClick\"\n      />\n    </el-option>\n  </el-select>\n</template>\n\n<script>\n  export default {\n    name: 'SelectTree',\n    props: {\n      props: {\n        type: Object,\n        default: () => {\n          return {\n            value: 'id',\n            label: 'title',\n            children: 'children'\n          }\n        }\n      },\n      options: {\n        type: Array,\n        default: () => {\n          return []\n        }\n      },\n      value: {\n        type: Number,\n        default: () => {\n          return null\n        }\n      },\n      clearable: {\n        type: Boolean,\n        default: () => {\n          return true\n        }\n      },\n      accordion: {\n        type: Boolean,\n        default: () => {\n          return false\n        }\n      }\n    },\n    data() {\n      return {\n        valueId: this.value,\n        valueTitle: '',\n        defaultExpandedKey: []\n      }\n    },\n    watch: {\n      value() {\n        this.valueId = this.value\n        this.initHandle()\n      }\n    },\n    mounted() {\n      this.initHandle()\n    },\n    methods: {\n      initHandle() {\n        if (this.valueId) {\n          this.valueTitle = this.$refs.selectTree.getNode(this.valueId).data[this.props.label]\n          this.$refs.selectTree.setCurrentKey(this.valueId)\n          this.defaultExpandedKey = [this.valueId]\n        }\n        this.initScroll()\n      },\n      initScroll() {\n        this.$nextTick(() => {\n          const scrollWrap = document.querySelectorAll('.el-scrollbar .el-select-dropdown__wrap')[0]\n          const scrollBar = document.querySelectorAll('.el-scrollbar .el-scrollbar__bar')\n          scrollWrap.style.cssText = 'margin: 0px; max-height: none; overflow: hidden;'\n          scrollBar.forEach(ele => {\n            ele.style.width = 0\n          })\n        })\n      },\n      handleNodeClick(node) {\n        this.valueTitle = node[this.props.label]\n        this.valueId = node[this.props.value]\n        this.$emit('getValue', this.valueId)\n        this.defaultExpandedKey = []\n      },\n      clearHandle() {\n        this.valueTitle = ''\n        this.valueId = null\n        this.defaultExpandedKey = []\n        this.clearSelected()\n        this.$emit('getValue', null)\n      },\n      clearSelected() {\n        const allNode = document.querySelectorAll('#tree-option .el-tree-node')\n        allNode.forEach((element) => element.classList.remove('is-current'))\n      }\n    }\n  }\n</script>\n\n<!-- Add \"scoped\" attribute to limit CSS to this component only -->\n<style scoped>\n  .el-scrollbar .el-scrollbar__view .el-select-dropdown__item {\n    height: auto;\n    max-height: 274px;\n    padding: 0;\n    overflow: hidden;\n    overflow-y: auto;\n  }\n\n  .el-select-dropdown__item.selected {\n    font-weight: normal;\n  }\n\n  ul li >>> .el-tree .el-tree-node__content {\n    height: auto;\n    padding: 0 20px;\n  }\n\n  .el-tree-node__label {\n    font-weight: normal;\n  }\n\n  .el-tree >>> .is-current .el-tree-node__label {\n    color: #409eff;\n    font-weight: 700;\n  }\n\n  .el-tree >>> .is-current .el-tree-node__children .el-tree-node__label {\n    color: #606266;\n    font-weight: normal;\n  }\n\n  /* 开发禁用 */\n  .el-tree-node:focus > .el-tree-node__content {\n    background-color: transparent;\n    background-color: #f5f7fa;\n    color: #c0c4cc;\n    cursor: not-allowed;\n  }\n\n  .el-tree-node__content:hover {\n    background-color: #f5f7fa;\n  }\n</style>\n"
  },
  {
    "path": "frontend/src/directive/clipboard/clipboard.js",
    "content": "// Inspired by https://github.com/Inndy/vue-clipboard2\nconst Clipboard = require('clipboard')\nif (!Clipboard) {\n  throw new Error('you should npm install `clipboard` --save at first ')\n}\n\nexport default {\n  bind(el, binding) {\n    if (binding.arg === 'success') {\n      el._v_clipboard_success = binding.value\n    } else if (binding.arg === 'error') {\n      el._v_clipboard_error = binding.value\n    } else {\n      const clipboard = new Clipboard(el, {\n        text() { return binding.value },\n        action() { return binding.arg === 'cut' ? 'cut' : 'copy' }\n      })\n      clipboard.on('success', e => {\n        const callback = el._v_clipboard_success\n        callback && callback(e) // eslint-disable-line\n      })\n      clipboard.on('error', e => {\n        const callback = el._v_clipboard_error\n        callback && callback(e) // eslint-disable-line\n      })\n      el._v_clipboard = clipboard\n    }\n  },\n  update(el, binding) {\n    if (binding.arg === 'success') {\n      el._v_clipboard_success = binding.value\n    } else if (binding.arg === 'error') {\n      el._v_clipboard_error = binding.value\n    } else {\n      el._v_clipboard.text = function() { return binding.value }\n      el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' }\n    }\n  },\n  unbind(el, binding) {\n    if (binding.arg === 'success') {\n      delete el._v_clipboard_success\n    } else if (binding.arg === 'error') {\n      delete el._v_clipboard_error\n    } else {\n      el._v_clipboard.destroy()\n      delete el._v_clipboard\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/src/directive/clipboard/index.js",
    "content": "import Clipboard from './clipboard'\n\nconst install = function(Vue) {\n  Vue.directive('Clipboard', Clipboard)\n}\n\nif (window.Vue) {\n  window.clipboard = Clipboard\n  Vue.use(install); // eslint-disable-line\n}\n\nClipboard.install = install\nexport default Clipboard\n"
  },
  {
    "path": "frontend/src/directive/sticky.js",
    "content": "const vueSticky = {}\nlet listenAction\nvueSticky.install = Vue => {\n  Vue.directive('sticky', {\n    inserted(el, binding) {\n      const params = binding.value || {}\n      const stickyTop = params.stickyTop || 0\n      const zIndex = params.zIndex || 1000\n      const elStyle = el.style\n\n      elStyle.position = '-webkit-sticky'\n      elStyle.position = 'sticky'\n      // if the browser support css sticky（Currently Safari, Firefox and Chrome Canary）\n      // if (~elStyle.position.indexOf('sticky')) {\n      //     elStyle.top = `${stickyTop}px`;\n      //     elStyle.zIndex = zIndex;\n      //     return\n      // }\n      const elHeight = el.getBoundingClientRect().height\n      const elWidth = el.getBoundingClientRect().width\n      elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`\n\n      const parentElm = el.parentNode || document.documentElement\n      const placeholder = document.createElement('div')\n      placeholder.style.display = 'none'\n      placeholder.style.width = `${elWidth}px`\n      placeholder.style.height = `${elHeight}px`\n      parentElm.insertBefore(placeholder, el)\n\n      let active = false\n\n      const getScroll = (target, top) => {\n        const prop = top ? 'pageYOffset' : 'pageXOffset'\n        const method = top ? 'scrollTop' : 'scrollLeft'\n        let ret = target[prop]\n        if (typeof ret !== 'number') {\n          ret = window.document.documentElement[method]\n        }\n        return ret\n      }\n\n      const sticky = () => {\n        if (active) {\n          return\n        }\n        if (!elStyle.height) {\n          elStyle.height = `${el.offsetHeight}px`\n        }\n\n        elStyle.position = 'fixed'\n        elStyle.width = `${elWidth}px`\n        placeholder.style.display = 'inline-block'\n        active = true\n      }\n\n      const reset = () => {\n        if (!active) {\n          return\n        }\n\n        elStyle.position = ''\n        placeholder.style.display = 'none'\n        active = false\n      }\n\n      const check = () => {\n        const scrollTop = getScroll(window, true)\n        const offsetTop = el.getBoundingClientRect().top\n        if (offsetTop < stickyTop) {\n          sticky()\n        } else {\n          if (scrollTop < elHeight + stickyTop) {\n            reset()\n          }\n        }\n      }\n      listenAction = () => {\n        check()\n      }\n\n      window.addEventListener('scroll', listenAction)\n    },\n\n    unbind() {\n      window.removeEventListener('scroll', listenAction)\n    }\n  })\n}\n\nexport default vueSticky\n\n"
  },
  {
    "path": "frontend/src/directive/waves/index.js",
    "content": "import waves from './waves'\n\nconst install = function(Vue) {\n  Vue.directive('waves', waves)\n}\n\nif (window.Vue) {\n  window.waves = waves\n  Vue.use(install); // eslint-disable-line\n}\n\nwaves.install = install\nexport default waves\n"
  },
  {
    "path": "frontend/src/directive/waves/waves.css",
    "content": ".waves-ripple {\n    position: absolute;\n    border-radius: 100%;\n    background-color: rgba(0, 0, 0, 0.15);\n    background-clip: padding-box;\n    pointer-events: none;\n    -webkit-user-select: none;\n    -moz-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n    -webkit-transform: scale(0);\n    -ms-transform: scale(0);\n    transform: scale(0);\n    opacity: 1;\n}\n\n.waves-ripple.z-active {\n    opacity: 0;\n    -webkit-transform: scale(2);\n    -ms-transform: scale(2);\n    transform: scale(2);\n    -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;\n    transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;\n    transition: opacity 1.2s ease-out, transform 0.6s ease-out;\n    transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;\n}"
  },
  {
    "path": "frontend/src/directive/waves/waves.js",
    "content": "import './waves.css'\n\nconst context = '@@wavesContext'\n\nfunction handleClick(el, binding) {\n  function handle(e) {\n    const customOpts = Object.assign({}, binding.value)\n    const opts = Object.assign({\n      ele: el, // 波纹作用元素\n      type: 'hit', // hit 点击位置扩散 center中心点扩展\n      color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色\n    },\n    customOpts\n    )\n    const target = opts.ele\n    if (target) {\n      target.style.position = 'relative'\n      target.style.overflow = 'hidden'\n      const rect = target.getBoundingClientRect()\n      let ripple = target.querySelector('.waves-ripple')\n      if (!ripple) {\n        ripple = document.createElement('span')\n        ripple.className = 'waves-ripple'\n        ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'\n        target.appendChild(ripple)\n      } else {\n        ripple.className = 'waves-ripple'\n      }\n      switch (opts.type) {\n        case 'center':\n          ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px'\n          ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px'\n          break\n        default:\n          ripple.style.top =\n            (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop ||\n              document.body.scrollTop) + 'px'\n          ripple.style.left =\n            (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft ||\n              document.body.scrollLeft) + 'px'\n      }\n      ripple.style.backgroundColor = opts.color\n      ripple.className = 'waves-ripple z-active'\n      return false\n    }\n  }\n\n  if (!el[context]) {\n    el[context] = {\n      removeHandle: handle\n    }\n  } else {\n    el[context].removeHandle = handle\n  }\n\n  return handle\n}\n\nexport default {\n  bind(el, binding) {\n    el.addEventListener('click', handleClick(el, binding), false)\n  },\n  update(el, binding) {\n    el.removeEventListener('click', el[context].removeHandle, false)\n    el.addEventListener('click', handleClick(el, binding), false)\n  },\n  unbind(el) {\n    el.removeEventListener('click', el[context].removeHandle, false)\n    el[context] = null\n    delete el[context]\n  }\n}\n"
  },
  {
    "path": "frontend/src/filters/index.js",
    "content": "// set function parseTime,formatTime to filter\nexport { parseTime, formatTime } from '@/utils'\n\nfunction pluralize(time, label) {\n  if (time === 1) {\n    return time + label\n  }\n  return time + label + 's'\n}\n\nexport function timeAgo(time) {\n  const between = Date.now() / 1000 - Number(time)\n  if (between < 3600) {\n    return pluralize(~~(between / 60), ' minute')\n  } else if (between < 86400) {\n    return pluralize(~~(between / 3600), ' hour')\n  } else {\n    return pluralize(~~(between / 86400), ' day')\n  }\n}\n\n/* 数字 格式化*/\nexport function numberFormatter(num, digits) {\n  const si = [\n    { value: 1E18, symbol: 'E' },\n    { value: 1E15, symbol: 'P' },\n    { value: 1E12, symbol: 'T' },\n    { value: 1E9, symbol: 'G' },\n    { value: 1E6, symbol: 'M' },\n    { value: 1E3, symbol: 'k' }\n  ]\n  for (let i = 0; i < si.length; i++) {\n    if (num >= si[i].value) {\n      return (num / si[i].value + 0.1).toFixed(digits).replace(/\\.0+$|(\\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol\n    }\n  }\n  return num.toString()\n}\n\nexport function toThousandFilter(num) {\n  return (+num || 0).toString().replace(/^-?\\d+/g, m => m.replace(/(?=(?!\\b)(\\d{3})+$)/g, ','))\n}\n\n// 格式化时间\nexport function parseDate(datestr) {\n  if (datestr !== undefined) {\n    const date = datestr.slice(0, 10)\n    const time = datestr.slice(11, 19)\n    return date + ' ' + time\n  }\n}\n\nexport function diffDate(date) {\n  const d1 = new Date()\n  const d2 = new Date(date)\n  return Math.round(parseInt(d2 - d1) / 1000 / 60 / 60 / 24)\n}\n\n// 菜单\nexport function menuTypeFilter(val) {\n  const Map = {\n    1: '模块',\n    2: '菜单',\n    3: '操作'\n  }\n  return Map[val]\n}\n\n// 按钮\nexport function operateTypeFilter(val) {\n  const Map = {\n    'none': '无',\n    'add': '新增',\n    'del': '删除',\n    'update': '编辑',\n    'view': '查看',\n  }\n  return Map[val]\n}\n\n// 字段类型\nexport function FieldTypeFilter(val) {\n  const Map = {\n    1: '字符串',\n    2: '整型',\n    3: '浮点型',\n    4: '布尔',\n    5: '日期',\n    6: '日期时间',\n    7: '范围日期',\n    8: '文本域',\n    9: '单选框',\n    10: '下拉列表',\n    11: '用户名',\n    12: '多选框',\n    13: '多选下拉',\n    14: '多选用户名',\n  }\n  return Map[val]\n}\n\n// 状态类型\nexport function StateTypeFilter(val) {\n  const Map = {\n    0: '普通状态',\n    1: '初始状态',\n    2: '结束状态',\n  }\n  return Map[val]\n}\n\n// 流转类型\nexport function TransitionTypeFilter(val) {\n  const Map = {\n    0: '常规流转',\n    1: '定时器流转',\n  }\n  return Map[val]\n}\n\n// 属性类型\nexport function AttributeTypeFilter(val) {\n  const Map = {\n    0: '草稿',\n    1: '待审',\n    2: '驳回',\n    3: '撤销',\n    4: '结束',\n    5: '已关闭',\n  }\n  return Map[val]\n}\n\n\n// 流转名称\nexport function TransitionNameFilter(val) {\n  const Map = {\n    0: '保存',\n    1: '转交下一步',\n    2: '驳回',\n    3: '撤销',\n    4: '关闭',\n  }\n  return Map[val]\n}\n\n// 取第一个字母并大写\nexport function AvatarFilter(val) {\n  return val.substr(0, 1).toUpperCase()\n}\n"
  },
  {
    "path": "frontend/src/icons/index.js",
    "content": "import Vue from 'vue'\nimport SvgIcon from '@/components/SvgIcon'// svg component\n\n// register globally\nVue.component('svg-icon', SvgIcon)\n\nconst req = require.context('./svg', false, /\\.svg$/)\nconst requireAll = requireContext => requireContext.keys().map(requireContext)\nrequireAll(req)\n"
  },
  {
    "path": "frontend/src/icons/svgo.yml",
    "content": "# replace default config\n\n# multipass: true\n# full: true\n\nplugins:\n\n  # - name\n  #\n  # or:\n  # - name: false\n  # - name: true\n  #\n  # or:\n  # - name:\n  #     param1: 1\n  #     param2: 2\n\n- removeAttrs:\n    attrs:\n      - 'fill'\n      - 'fill-rule'\n"
  },
  {
    "path": "frontend/src/lang/en.js",
    "content": "export default {\n  systemTitle: 'OMS',\n  \n  route: {\n    login: 'Login',\n    dashboard: 'Dashboard',\n    sys: 'sysManager',\n    group: 'groupManager',\n    user: 'userManager',\n    role: 'roleManager',\n    menu: 'menuManager',\n    icon: 'iconManager',\n    tool: 'tool',\n    audit: 'audit',\n    test: 'testManager',\n    workflow: 'workflow',\n    wfset: 'wfset',\n    wfconf: 'wfconf',\n    wftype: 'wftype',\n    ticket: 'ticket',\n    new_ticket: 'new_ticket',\n    u_ticket: 'u_ticket',\n    s_ticket: 's_ticket',\n    my_ticket: 'my_ticket',\n    todo_ticket: 'todo_ticket',\n    all_ticket: 'all_ticket',\n    notice: 'notice',\n    mail: 'mail',\n    telegram: 'telegram',\n    ComponentDemo: 'ComponentDemo',\n  },\n  navbar: {\n    logOut: 'Log Out',\n    dashboard: 'Dashboard',\n    github: 'Github',\n    theme: 'Theme',\n    size: 'Global Size'\n  },\n  login: {\n    title: 'Login Form',\n    logIn: 'Login',\n    username: 'Username',\n    password: 'Password',\n    any: 'any',\n    thirdparty: 'Or connect with',\n    thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !'\n  },\n  documentation: {\n    documentation: 'Documentation',\n    github: 'Github Repository'\n  },\n  permission: {\n    addRole: 'New Role',\n    editPermission: 'Edit',\n    roles: 'Your roles',\n    switchRoles: 'Switch roles',\n    tips: 'In some cases, using v-permission will have no effect. For example: Element-UI  el-tab or el-table-column and other scenes that dynamically render dom. You can only do this with v-if.',\n    delete: 'Delete',\n    confirm: 'Confirm',\n    cancel: 'Cancel'\n  },\n  guide: {\n    description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',\n    button: 'Show Guide'\n  },\n  components: {\n    documentation: 'Documentation',\n    tinymceTips: 'Rich text is a core feature of the management backend, but at the same time it is a place with lots of pits. In the process of selecting rich texts, I also took a lot of detours. The common rich texts on the market have been basically used, and I finally chose Tinymce. See the more detailed rich text comparison and introduction.',\n    dropzoneTips: 'Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.',\n    stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.',\n    backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',\n    backToTopTips2: 'You can customize the style of the button, show / hide, height of appearance, height of the return. If you need a text prompt, you can use element-ui el-tooltip elements externally',\n    imageUploadTips: 'Since I was using only the vue@1 version, and it is not compatible with mockjs at the moment, I modified it myself, and if you are going to use it, it is better to use official version.'\n  },\n  table: {\n    dynamicTips1: 'Fixed header, sorted by header order',\n    dynamicTips2: 'Not fixed header, sorted by click order',\n    dragTips1: 'The default order',\n    dragTips2: 'The after dragging order',\n    title: 'Title',\n    importance: 'Imp',\n    type: 'Type',\n    remark: 'Remark',\n    search: 'Search',\n    add: 'Add',\n    export: 'Export',\n    reviewer: 'reviewer',\n    id: 'ID',\n    date: 'Date',\n    author: 'Author',\n    readings: 'Readings',\n    status: 'Status',\n    actions: 'Actions',\n    edit: 'Edit',\n    publish: 'Publish',\n    draft: 'Draft',\n    delete: 'Delete',\n    cancel: 'Cancel',\n    confirm: 'Confirm'\n  },\n  errorLog: {\n    tips: 'Please click the bug icon in the upper right corner',\n    description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.',\n    documentation: 'Document introduction'\n  },\n  excel: {\n    export: 'Export',\n    selectedExport: 'Export Selected Items',\n    placeholder: 'Please enter the file name (default excel-list)'\n  },\n  zip: {\n    export: 'Export',\n    placeholder: 'Please enter the file name (default file)'\n  },\n  pdf: {\n    tips: 'Here we use window.print() to implement the feature of downloading PDF.'\n  },\n  theme: {\n    change: 'Change Theme',\n    documentation: 'Theme documentation',\n    tips: 'Tips: It is different from the theme-pick on the navbar is two different skinning methods, each with different application scenarios. Refer to the documentation for details.'\n  },\n  tagsView: {\n    refresh: 'Refresh',\n    close: 'Close',\n    closeOthers: 'Close Others',\n    closeAll: 'Close All'\n  },\n  settings: {\n    title: 'Page style setting',\n    theme: 'Theme Color',\n    tagsView: 'Open Tags-View',\n    fixedHeader: 'Fixed Header',\n    sidebarLogo: 'Sidebar Logo'\n  }\n}\n"
  },
  {
    "path": "frontend/src/lang/index.js",
    "content": "import Vue from 'vue'\nimport VueI18n from 'vue-i18n'\nimport Cookies from 'js-cookie'\nimport elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang\nimport elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang\nimport enLocale from './en'\nimport zhLocale from './zh'\n\nVue.use(VueI18n)\n\nconst messages = {\n  en: {\n    ...enLocale,\n    ...elementEnLocale\n  },\n  zh: {\n    ...zhLocale,\n    ...elementZhLocale\n  }\n}\nexport function getLanguage() {\n  const chooseLanguage = Cookies.get('language')\n  if (chooseLanguage) return chooseLanguage\n\n  // if has not choose language\n  const language = (navigator.language || navigator.browserLanguage).toLowerCase()\n  const locales = Object.keys(messages)\n  for (const locale of locales) {\n    if (language.indexOf(locale) > -1) {\n      return locale\n    }\n  }\n  return 'zh'\n}\nconst i18n = new VueI18n({\n  // set locale\n  // options: en | zh | es\n  locale: getLanguage(),\n  // set locale messages\n  messages,\n  silentTranslationWarn: true\n})\n\nexport default i18n\n"
  },
  {
    "path": "frontend/src/lang/zh.js",
    "content": "export default {\n  systemTitle: '后台管理系统',\n\n  route: {\n    login: '登录',\n    dashboard: '首页',\n    sys: '系统管理',\n    group: '分组管理',\n    user: '用户管理',\n    role: '角色管理',\n    menu: '菜单管理',\n    icon: '图标管理',\n    tool: '工具管理',\n    audit: '审计日志',\n    test: '测试页面',\n    workflow: '工作流',\n    wfset: '工作流设计',\n    wfconf: '工作流配置',\n    wftype: '工作流类型',\n    ticket: '工单系统',\n    new_ticket: '新建工单',\n    u_ticket: '编辑工单',\n    s_ticket: '审批工单',\n    my_ticket: '我创建的',\n    todo_ticket: '我的待办',\n    all_ticket: '所有工单',\n    notice: '通知管理',\n    mail: 'mail通知',\n    telegram: 'telegram通知',\n    ComponentDemo: '组件例子',\n  },\n  navbar: {\n    logOut: '退出登录',\n    dashboard: '首页',\n    github: '项目地址',\n    theme: '换肤',\n    size: '布局大小'\n  },\n  login: {\n    title: '系统登录',\n    logIn: '登录',\n    username: '账号',\n    password: '密码',\n    any: '随便填',\n    thirdparty: '第三方登录',\n    thirdpartyTips: '本地不能模拟，请结合自己业务进行模拟！！！'\n  },\n  documentation: {\n    documentation: '文档',\n    github: 'Github 地址'\n  },\n  permission: {\n    addRole: '新增角色',\n    editPermission: '编辑权限',\n    roles: '你的权限',\n    switchRoles: '切换权限',\n    tips: '在某些情况下，不适合使用 v-permission。例如：Element-UI 的 el-tab 或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。',\n    delete: '删除',\n    confirm: '确定',\n    cancel: '取消'\n  },\n  guide: {\n    description: '引导页对于一些第一次进入项目的人很有用，你可以简单介绍下项目的功能。本 Demo 是基于',\n    button: '打开引导'\n  },\n  components: {\n    documentation: '文档',\n    tinymceTips: '富文本是管理后台一个核心的功能，但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路，市面上常见的富文本都基本用过了，最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见',\n    dropzoneTips: '由于我司业务有特殊需求，而且要传七牛 所以没用第三方，选择了自己封装。代码非常的简单，具体代码你可以在这里看到 @/components/Dropzone',\n    stickyTips: '当页面滚动到预设的位置会吸附在顶部',\n    backToTopTips1: '页面滚动到指定位置会在右下角出现返回顶部按钮',\n    backToTopTips2: '可自定义按钮的样式、show/hide、出现的高度、返回的位置 如需文字提示，可在外部使用Element的el-tooltip元素',\n    imageUploadTips: '由于我在使用时它只有vue@1版本，而且和mockjs不兼容，所以自己改造了一下，如果大家要使用的话，优先还是使用官方版本。'\n  },\n  table: {\n    dynamicTips1: '固定表头, 按照表头顺序排序',\n    dynamicTips2: '不固定表头, 按照点击顺序排序',\n    dragTips1: '默认顺序',\n    dragTips2: '拖拽后顺序',\n    title: '标题',\n    importance: '重要性',\n    type: '类型',\n    remark: '点评',\n    search: '搜索',\n    add: '添加',\n    export: '导出',\n    reviewer: '审核人',\n    id: '序号',\n    date: '时间',\n    author: '作者',\n    readings: '阅读数',\n    status: '状态',\n    actions: '操作',\n    edit: '编辑',\n    publish: '发布',\n    draft: '草稿',\n    delete: '删除',\n    cancel: '取 消',\n    confirm: '确 定'\n  },\n  errorLog: {\n    tips: '请点击右上角bug小图标',\n    description: '现在的管理后台基本都是spa的形式了，它增强了用户体验，但同时也会增加页面出问题的可能性，可能一个小小的疏忽就导致整个页面的死锁。好在 Vue 官网提供了一个方法来捕获处理异常，你可以在其中进行错误处理或者异常上报。',\n    documentation: '文档介绍'\n  },\n  excel: {\n    export: '导出',\n    selectedExport: '导出已选择项',\n    placeholder: '请输入文件名(默认excel-list)'\n  },\n  zip: {\n    export: '导出',\n    placeholder: '请输入文件名(默认file)'\n  },\n  pdf: {\n    tips: '这里使用   window.print() 来实现下载pdf的功能'\n  },\n  theme: {\n    change: '换肤',\n    documentation: '换肤文档',\n    tips: 'Tips: 它区别于 navbar 上的 theme-pick, 是两种不同的换肤方法，各自有不同的应用场景，具体请参考文档。'\n  },\n  tagsView: {\n    refresh: '刷新',\n    close: '关闭',\n    closeOthers: '关闭其它',\n    closeAll: '关闭所有'\n  },\n  settings: {\n    title: '系统布局配置',\n    theme: '主题色',\n    tagsView: '开启 Tags-View',\n    fixedHeader: '固定 Header',\n    sidebarLogo: '侧边栏 Logo'\n  }\n}\n"
  },
  {
    "path": "frontend/src/layout/components/AppMain.vue",
    "content": "<template>\n  <section class=\"app-main\">\n    <transition name=\"fade-transform\" mode=\"out-in\">\n      <keep-alive :include=\"cachedViews\">\n        <router-view :key=\"key\" />\n      </keep-alive>\n    </transition>\n  </section>\n</template>\n\n<script>\nexport default {\n  name: 'AppMain',\n  computed: {\n    cachedViews() {\n      return this.$store.state.tagsView.cachedViews\n    },\n    key() {\n      return this.$route.path\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.app-main {\n  /* 50= navbar  50  */\n  min-height: calc(100vh - 50px);\n  width: 100%;\n  position: relative;\n  overflow: hidden;\n}\n\n.fixed-header+.app-main {\n  padding-top: 50px;\n}\n\n.hasTagsView {\n  .app-main {\n    /* 84 = navbar + tags-view = 50 + 34 */\n    min-height: calc(100vh - 84px);\n  }\n\n  .fixed-header+.app-main {\n    padding-top: 84px;\n  }\n}\n</style>\n\n<style lang=\"scss\">\n// fix css style bug in open el-dialog\n.el-popup-parent--hidden {\n  .fixed-header {\n    padding-right: 15px;\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/Navbar.vue",
    "content": "<template>\n  <div class=\"navbar\">\n    <hamburger\n      id=\"hamburger-container\"\n      :is-active=\"sidebar.opened\"\n      class=\"hamburger-container\"\n      @toggleClick=\"toggleSideBar\"\n    />\n\n    <breadcrumb id=\"breadcrumb-container\" class=\"breadcrumb-container\" />\n\n    <div class=\"right-menu\">\n      <template v-if=\"device !== 'mobile'\">\n        <div class=\"right-menu-item\">\n          <a class=\"ip\">{{ip}}</a>\n          <a class=\"date\">{{cur_date}}</a>\n        </div>\n\n        <search id=\"header-search\" class=\"right-menu-item\" />\n\n        <screenfull id=\"screenfull\" class=\"right-menu-item hover-effect\" />\n\n        <lang-select class=\"right-menu-item hover-effect\" />\n      </template>\n\n      <el-dropdown class=\"avatar-container right-menu-item hover-effect\" trigger=\"click\">\n        <div class=\"avatar-wrapper\">\n          <el-avatar :src=\"avatar\" />\n        </div>\n        <el-dropdown-menu slot=\"dropdown\">\n          <router-link to=\"/\">\n            <el-dropdown-item>{{ \"首页\" }}</el-dropdown-item>\n          </router-link>\n          <el-dropdown-item disabled>{{\"个人中心\"}}</el-dropdown-item>\n          <el-dropdown-item divided>\n            <span style=\"display:block;\" @click=\"logout\">{{ \"退出登录\" }}</span>\n          </el-dropdown-item>\n        </el-dropdown-menu>\n      </el-dropdown>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { mapGetters } from \"vuex\";\nimport Breadcrumb from \"@/components/Breadcrumb\";\nimport Hamburger from \"@/components/Hamburger\";\nimport Screenfull from \"@/components/Screenfull\";\nimport LangSelect from \"@/components/LangSelect\";\nimport Search from \"@/components/HeaderSearch\";\n\nexport default {\n  components: {\n    Breadcrumb,\n    Hamburger,\n    Screenfull,\n    LangSelect,\n    Search\n  },\n  data() {\n    return {\n      cur_date: \"\"\n    };\n  },\n  computed: {\n    ...mapGetters([\"sidebar\", \"name\", \"avatar\", \"ip\", \"device\"])\n  },\n  methods: {\n    toggleSideBar() {\n      this.$store.dispatch(\"app/toggleSideBar\");\n    },\n    async logout() {\n      await this.$store.dispatch(\"user/logout\");\n      this.$router.push(`/login?redirect=${this.$route.fullPath}`);\n    }\n  },\n  mounted() {\n    var _this = this; //声明一个变量指向vue实例this,保证作用域一致\n    this.timer = setInterval(function() {\n      _this.cur_date = new Date().toLocaleString(); //修改数据date\n    }, 1000);\n  }\n};\n</script>\n\n<style lang=\"scss\" scoped>\n.navbar {\n  height: 50px;\n  overflow: hidden;\n  position: relative;\n  background: #fff;\n  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);\n\n  .hamburger-container {\n    line-height: 46px;\n    height: 100%;\n    float: left;\n    cursor: pointer;\n    transition: background 0.3s;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover {\n      background: rgba(0, 0, 0, 0.025);\n    }\n  }\n\n  .breadcrumb-container {\n    float: left;\n  }\n\n  .errLog-container {\n    display: inline-block;\n    vertical-align: top;\n  }\n\n  .right-menu {\n    float: right;\n    height: 100%;\n    line-height: 50px;\n\n    &:focus {\n      outline: none;\n    }\n\n    .right-menu-item {\n      display: inline-block;\n      padding: 0 8px;\n      height: 100%;\n      font-size: 18px;\n      color: #5a5e66;\n      vertical-align: text-bottom;\n\n        .ip {\n          color: #39b3d1;\n        }\n\n        .date {\n          color: #d39011;\n        }\n\n      &.hover-effect {\n        cursor: pointer;\n        transition: background 0.3s;\n\n        &:hover {\n          background: rgba(0, 0, 0, 0.025);\n        }\n      }\n    }\n\n    .avatar-container {\n      margin-right: 30px;\n\n      .avatar-wrapper {\n        margin-top: 5px;\n        position: relative;\n\n        .user-avatar {\n          cursor: pointer;\n          width: 40px;\n          height: 40px;\n          border-radius: 10px;\n        }\n\n        .el-icon-caret-bottom {\n          cursor: pointer;\n          position: absolute;\n          right: -20px;\n          top: 25px;\n          font-size: 12px;\n        }\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/Settings/index.vue",
    "content": "<template>\n  <div class=\"drawer-container\">\n    <div>\n      <h3 class=\"drawer-title\">{{ $t('settings.title') }}</h3>\n\n      <div class=\"drawer-item\">\n        <span>{{ $t('settings.theme') }}</span>\n        <theme-picker style=\"float: right;height: 26px;margin: -3px 8px 0 0;\" @change=\"themeChange\" />\n      </div>\n\n      <div class=\"drawer-item\">\n        <span>{{ $t('settings.tagsView') }}</span>\n        <el-switch v-model=\"tagsView\" class=\"drawer-switch\" />\n      </div>\n\n      <div class=\"drawer-item\">\n        <span>{{ $t('settings.fixedHeader') }}</span>\n        <el-switch v-model=\"fixedHeader\" class=\"drawer-switch\" />\n      </div>\n\n      <div class=\"drawer-item\">\n        <span>{{ $t('settings.sidebarLogo') }}</span>\n        <el-switch v-model=\"sidebarLogo\" class=\"drawer-switch\" />\n      </div>\n\n    </div>\n  </div>\n</template>\n\n<script>\nimport ThemePicker from '@/components/ThemePicker'\n\nexport default {\n  components: { ThemePicker },\n  data() {\n    return {}\n  },\n  computed: {\n    fixedHeader: {\n      get() {\n        return this.$store.state.settings.fixedHeader\n      },\n      set(val) {\n        this.$store.dispatch('settings/changeSetting', {\n          key: 'fixedHeader',\n          value: val\n        })\n      }\n    },\n    tagsView: {\n      get() {\n        return this.$store.state.settings.tagsView\n      },\n      set(val) {\n        this.$store.dispatch('settings/changeSetting', {\n          key: 'tagsView',\n          value: val\n        })\n      }\n    },\n    sidebarLogo: {\n      get() {\n        return this.$store.state.settings.sidebarLogo\n      },\n      set(val) {\n        this.$store.dispatch('settings/changeSetting', {\n          key: 'sidebarLogo',\n          value: val\n        })\n      }\n    }\n  },\n  methods: {\n    themeChange(val) {\n      this.$store.dispatch('settings/changeSetting', {\n        key: 'theme',\n        value: val\n      })\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.drawer-container {\n  padding: 24px;\n  font-size: 14px;\n  line-height: 1.5;\n  word-wrap: break-word;\n  background-color: #e8f4ff94;\n\n  .drawer-title {\n    margin-bottom: 12px;\n    color: #a030ed;\n    font-size: 18px;\n    line-height: 22px;\n  }\n\n  .drawer-item {\n    color: #ed1c90;\n    font-size: 14px;\n    font-weight: bold;\n    padding: 12px 0;\n  }\n\n  .drawer-switch {\n    float: right\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/Sidebar/Item.vue",
    "content": "<script>\nexport default {\n  name: 'MenuItem',\n  functional: true,\n  props: {\n    icon: {\n      type: String,\n      default: ''\n    },\n    title: {\n      type: String,\n      default: ''\n    }\n  },\n  render(h, context) {\n    const { icon, title } = context.props\n    const vnodes = []\n\n    if (icon) {\n      vnodes.push(<svg-icon icon-class={icon}/>)\n    }\n\n    if (title) {\n      vnodes.push(<span slot='title'>{(title)}</span>)\n    }\n    return vnodes\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/layout/components/Sidebar/Link.vue",
    "content": "\n<template>\n  <!-- eslint-disable vue/require-component-is -->\n  <component v-bind=\"linkProps(to)\">\n    <slot />\n  </component>\n</template>\n\n<script>\nimport { isExternal } from '@/utils/validate'\n\nexport default {\n  props: {\n    to: {\n      type: String,\n      required: true\n    }\n  },\n  methods: {\n    linkProps(url) {\n      if (isExternal(url)) {\n        return {\n          is: 'a',\n          href: url,\n          target: '_blank',\n          rel: 'noopener'\n        }\n      }\n      return {\n        is: 'router-link',\n        to: url\n      }\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/layout/components/Sidebar/Logo.vue",
    "content": "<template>\n  <div class=\"sidebar-logo-container\" :class=\"{'collapse':collapse}\">\n    <transition name=\"sidebarLogoFade\">\n      <router-link v-if=\"collapse\" key=\"collapse\" class=\"sidebar-logo-link\" to=\"/\">\n        <img v-if=\"logo\" :src=\"logo\" class=\"sidebar-logo\" />\n        <h1 v-else class=\"sidebar-title\">{{ title }}</h1>\n      </router-link>\n      <router-link v-else key=\"expand\" class=\"sidebar-logo-link\" to=\"/\">\n        <img v-if=\"logo\" :src=\"logo\" class=\"sidebar-logo\" />\n        <h1 class=\"sidebar-title\">{{ title }}</h1>\n      </router-link>\n    </transition>\n  </div>\n</template>\n\n<script>\nimport defaultSettings from \"@/settings\";\n\nconst title = defaultSettings.title || \"Vue Element Admin\";\n\nexport default {\n  name: \"SidebarLogo\",\n  props: {\n    collapse: {\n      type: Boolean,\n      required: true\n    }\n  },\n  data() {\n    return {\n      title: title,\n      logo:\"http://softqual.co.il/wp-content/uploads/2017/06/cropped-favicon.png\"\n    };\n  }\n};\n</script>\n\n<style lang=\"scss\" scoped>\n.sidebarLogoFade-enter-active {\n  transition: opacity 1.5s;\n}\n\n.sidebarLogoFade-enter,\n.sidebarLogoFade-leave-to {\n  opacity: 0;\n}\n\n.sidebar-logo-container {\n  position: relative;\n  width: 100%;\n  height: 50px;\n  line-height: 50px;\n  background: #2b2f3a;\n  text-align: center;\n  overflow: hidden;\n\n  & .sidebar-logo-link {\n    height: 100%;\n    width: 100%;\n\n    & .sidebar-logo {\n      width: 32px;\n      height: 32px;\n      vertical-align: middle;\n      margin-right: 12px;\n    }\n\n    & .sidebar-title {\n      display: inline-block;\n      margin: 0;\n      color: #fff;\n      font-weight: 600;\n      line-height: 50px;\n      font-size: 14px;\n      font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;\n      vertical-align: middle;\n    }\n  }\n\n  &.collapse {\n    .sidebar-logo {\n      margin-right: 0px;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/Sidebar/SidebarItem.vue",
    "content": "<template>\n  <div v-if=\"!item.hidden\" class=\"menu-wrapper\">\n    <template v-if=\"hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow\">\n      <app-link v-if=\"onlyOneChild.meta\" :to=\"resolvePath(onlyOneChild.path)\">\n        <el-menu-item :index=\"resolvePath(onlyOneChild.path)\" :class=\"{'submenu-title-noDropdown':!isNest}\">\n          <item :icon=\"onlyOneChild.meta.icon||(item.meta&&item.meta.icon)\" :title=\"generateTitle(onlyOneChild.meta.title)\" />\n        </el-menu-item>\n      </app-link>\n    </template>\n\n    <el-submenu v-else ref=\"subMenu\" :index=\"resolvePath(item.path)\" popper-append-to-body>\n      <template slot=\"title\">\n        <item v-if=\"item.meta\" :icon=\"item.meta && item.meta.icon\" :title=\"generateTitle(item.meta.title)\" />\n      </template>\n      <sidebar-item\n        v-for=\"child in item.children\"\n        :key=\"child.path\"\n        :is-nest=\"true\"\n        :item=\"child\"\n        :base-path=\"resolvePath(child.path)\"\n        class=\"nest-menu\"\n      />\n    </el-submenu>\n  </div>\n</template>\n\n<script>\nimport path from 'path'\nimport { generateTitle } from '@/utils/i18n'\nimport { isExternal } from '@/utils/validate'\nimport Item from './Item'\nimport AppLink from './Link'\n\nexport default {\n  name: 'SidebarItem',\n  components: { Item, AppLink },\n  props: {\n    // route object\n    item: {\n      type: Object,\n      required: true\n    },\n    isNest: {\n      type: Boolean,\n      default: false\n    },\n    basePath: {\n      type: String,\n      default: ''\n    }\n  },\n  data() {\n    // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237\n    // TODO: refactor with render function\n    this.onlyOneChild = null\n    return {}\n  },\n  methods: {\n    hasOneShowingChild(children = [], parent) {\n      const showingChildren = children.filter(item => {\n        if (item.hidden) {\n          return false\n        } else {\n          // Temp set(will be used if only has one showing child)\n          this.onlyOneChild = item\n          return true\n        }\n      })\n\n      // When there is only one child router, the child router is displayed by default\n      if (showingChildren.length === 1) {\n        return true\n      }\n\n      // Show parent if there are no child router to display\n      if (showingChildren.length === 0) {\n        this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }\n        return true\n      }\n\n      return false\n    },\n    resolvePath(routePath) {\n      if (isExternal(routePath)) {\n        return routePath\n      }\n      return path.resolve(this.basePath, routePath)\n    },\n\n    generateTitle\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/layout/components/Sidebar/index.vue",
    "content": "<template>\n  <div :class=\"{'has-logo':showLogo}\">\n    <logo v-if=\"showLogo\" :collapse=\"isCollapse\" />\n    <el-scrollbar wrap-class=\"scrollbar-wrapper\">\n      <el-menu\n        :default-active=\"activeMenu\"\n        :collapse=\"isCollapse\"\n        :background-color=\"variables.menuBg\"\n        :text-color=\"variables.menuText\"\n        :active-text-color=\"variables.menuActiveText\"\n        :collapse-transition=\"false\"\n        :unique-opened=\"true\"\n        mode=\"vertical\"\n      >\n        <sidebar-item v-for=\"route in permission_routes\" :key=\"route.path\" :item=\"route\" :base-path=\"route.path\" />\n      </el-menu>\n    </el-scrollbar>\n  </div>\n</template>\n\n<script>\nimport { mapGetters } from 'vuex'\nimport Logo from './Logo'\nimport SidebarItem from './SidebarItem'\nimport variables from '@/styles/variables.scss'\n\nexport default {\n  components: { SidebarItem, Logo },\n  computed: {\n    ...mapGetters([\n      'permission_routes',\n      'sidebar'\n    ]),\n    activeMenu() {\n      const route = this.$route\n      const { meta, path } = route\n      // if set path, the sidebar will highlight the path you set\n      if (meta.activeMenu) {\n        return meta.activeMenu\n      }\n      return path\n    },\n    showLogo() {\n      return this.$store.state.settings.sidebarLogo\n    },\n    variables() {\n      return variables\n    },\n    isCollapse() {\n      return !this.sidebar.opened\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/layout/components/TagsView/ScrollPane.vue",
    "content": "<template>\n  <el-scrollbar ref=\"scrollContainer\" :vertical=\"false\" class=\"scroll-container\" @wheel.native.prevent=\"handleScroll\">\n    <slot />\n  </el-scrollbar>\n</template>\n\n<script>\nconst tagAndTagSpacing = 4 // tagAndTagSpacing\n\nexport default {\n  name: 'ScrollPane',\n  data() {\n    return {\n      left: 0\n    }\n  },\n  computed: {\n    scrollWrapper() {\n      return this.$refs.scrollContainer.$refs.wrap\n    }\n  },\n  methods: {\n    handleScroll(e) {\n      const eventDelta = e.wheelDelta || -e.deltaY * 40\n      const $scrollWrapper = this.scrollWrapper\n      $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4\n    },\n    moveToTarget(currentTag) {\n      const $container = this.$refs.scrollContainer.$el\n      const $containerWidth = $container.offsetWidth\n      const $scrollWrapper = this.scrollWrapper\n      const tagList = this.$parent.$refs.tag\n\n      let firstTag = null\n      let lastTag = null\n\n      // find first tag and last tag\n      if (tagList.length > 0) {\n        firstTag = tagList[0]\n        lastTag = tagList[tagList.length - 1]\n      }\n\n      if (firstTag === currentTag) {\n        $scrollWrapper.scrollLeft = 0\n      } else if (lastTag === currentTag) {\n        $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth\n      } else {\n        // find preTag and nextTag\n        const currentIndex = tagList.findIndex(item => item === currentTag)\n        const prevTag = tagList[currentIndex - 1]\n        const nextTag = tagList[currentIndex + 1]\n\n        // the tag's offsetLeft after of nextTag\n        const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing\n\n        // the tag's offsetLeft before of prevTag\n        const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing\n\n        if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {\n          $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth\n        } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {\n          $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft\n        }\n      }\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.scroll-container {\n  white-space: nowrap;\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n  /deep/ {\n    .el-scrollbar__bar {\n      bottom: 0px;\n    }\n    .el-scrollbar__wrap {\n      height: 49px;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/TagsView/index.vue",
    "content": "<template>\n  <div id=\"tags-view-container\" class=\"tags-view-container\">\n    <scroll-pane ref=\"scrollPane\" class=\"tags-view-wrapper\">\n      <router-link\n        v-for=\"tag in visitedViews\"\n        ref=\"tag\"\n        :key=\"tag.path\"\n        :class=\"isActive(tag)?'active':''\"\n        :to=\"{ path: tag.path, query: tag.query, fullPath: tag.fullPath }\"\n        tag=\"span\"\n        class=\"tags-view-item\"\n        @click.middle.native=\"!isAffix(tag)?closeSelectedTag(tag):''\"\n        @contextmenu.prevent.native=\"openMenu(tag,$event)\"\n      >\n        {{ generateTitle(tag.title) }}\n        <span v-if=\"!isAffix(tag)\" class=\"el-icon-close\" @click.prevent.stop=\"closeSelectedTag(tag)\" />\n      </router-link>\n    </scroll-pane>\n    <ul v-show=\"visible\" :style=\"{left:left+'px',top:top+'px'}\" class=\"contextmenu\">\n      <li @click=\"refreshSelectedTag(selectedTag)\">{{ $t('tagsView.refresh') }}</li>\n      <li v-if=\"!isAffix(selectedTag)\" @click=\"closeSelectedTag(selectedTag)\">{{ $t('tagsView.close') }}</li>\n      <li @click=\"closeOthersTags\">{{ $t('tagsView.closeOthers') }}</li>\n      <li @click=\"closeAllTags(selectedTag)\">{{ $t('tagsView.closeAll') }}</li>\n    </ul>\n  </div>\n</template>\n\n<script>\nimport ScrollPane from './ScrollPane'\nimport { generateTitle } from '@/utils/i18n'\nimport path from 'path'\n\nexport default {\n  components: { ScrollPane },\n  data() {\n    return {\n      visible: false,\n      top: 0,\n      left: 0,\n      selectedTag: {},\n      affixTags: []\n    }\n  },\n  computed: {\n    visitedViews() {\n      return this.$store.state.tagsView.visitedViews\n    },\n    routes() {\n      return this.$store.state.permission.routes\n    }\n  },\n  watch: {\n    $route() {\n      this.addTags()\n      this.moveToCurrentTag()\n    },\n    visible(value) {\n      if (value) {\n        document.body.addEventListener('click', this.closeMenu)\n      } else {\n        document.body.removeEventListener('click', this.closeMenu)\n      }\n    }\n  },\n  mounted() {\n    this.initTags()\n    this.addTags()\n  },\n  methods: {\n    generateTitle,\n    isActive(route) {\n      return route.path === this.$route.path\n    },\n    isAffix(tag) {\n      return tag.meta && tag.meta.affix\n    },\n    filterAffixTags(routes, basePath = '/') {\n      let tags = []\n      routes.forEach(route => {\n        if (route.meta && route.meta.affix) {\n          const tagPath = path.resolve(basePath, route.path)\n          tags.push({\n            fullPath: tagPath,\n            path: tagPath,\n            name: route.name,\n            meta: { ...route.meta }\n          })\n        }\n        if (route.children) {\n          const tempTags = this.filterAffixTags(route.children, route.path)\n          if (tempTags.length >= 1) {\n            tags = [...tags, ...tempTags]\n          }\n        }\n      })\n      return tags\n    },\n    initTags() {\n      const affixTags = this.affixTags = this.filterAffixTags(this.routes)\n      for (const tag of affixTags) {\n        // Must have tag name\n        if (tag.name) {\n          this.$store.dispatch('tagsView/addVisitedView', tag)\n        }\n      }\n    },\n    addTags() {\n      const { name } = this.$route\n      if (name) {\n        this.$store.dispatch('tagsView/addView', this.$route)\n      }\n      return false\n    },\n    moveToCurrentTag() {\n      const tags = this.$refs.tag\n      this.$nextTick(() => {\n        for (const tag of tags) {\n          if (tag.to.path === this.$route.path) {\n            this.$refs.scrollPane.moveToTarget(tag)\n            // when query is different then update\n            if (tag.to.fullPath !== this.$route.fullPath) {\n              this.$store.dispatch('tagsView/updateVisitedView', this.$route)\n            }\n            break\n          }\n        }\n      })\n    },\n    refreshSelectedTag(view) {\n      this.$store.dispatch('tagsView/delCachedView', view).then(() => {\n        const { fullPath } = view\n        this.$nextTick(() => {\n          this.$router.replace({\n            path: '/redirect' + fullPath\n          })\n        })\n      })\n    },\n    closeSelectedTag(view) {\n      this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {\n        if (this.isActive(view)) {\n          this.toLastView(visitedViews, view)\n        }\n      })\n    },\n    closeOthersTags() {\n      this.$router.push(this.selectedTag)\n      this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {\n        this.moveToCurrentTag()\n      })\n    },\n    closeAllTags(view) {\n      this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {\n        if (this.affixTags.some(tag => tag.path === view.path)) {\n          return\n        }\n        this.toLastView(visitedViews, view)\n      })\n    },\n    toLastView(visitedViews, view) {\n      const latestView = visitedViews.slice(-1)[0]\n      if (latestView) {\n        this.$router.push(latestView.fullPath)\n      } else {\n        // now the default is to redirect to the home page if there is no tags-view,\n        // you can adjust it according to your needs.\n        if (view.name === 'Dashboard') {\n          // to reload home page\n          this.$router.replace({ path: '/redirect' + view.fullPath })\n        } else {\n          this.$router.push('/')\n        }\n      }\n    },\n    openMenu(tag, e) {\n      const menuMinWidth = 105\n      const offsetLeft = this.$el.getBoundingClientRect().left // container margin left\n      const offsetWidth = this.$el.offsetWidth // container width\n      const maxLeft = offsetWidth - menuMinWidth // left boundary\n      const left = e.clientX - offsetLeft + 15 // 15: margin right\n\n      if (left > maxLeft) {\n        this.left = maxLeft\n      } else {\n        this.left = left\n      }\n\n      this.top = e.clientY\n      this.visible = true\n      this.selectedTag = tag\n    },\n    closeMenu() {\n      this.visible = false\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.tags-view-container {\n  height: 34px;\n  width: 100%;\n  background: #fff;\n  border-bottom: 1px solid #d8dce5;\n  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);\n  .tags-view-wrapper {\n    .tags-view-item {\n      display: inline-block;\n      position: relative;\n      cursor: pointer;\n      height: 26px;\n      line-height: 26px;\n      border: 1px solid #d8dce5;\n      color: #495060;\n      background: #fff;\n      padding: 0 8px;\n      font-size: 12px;\n      margin-left: 5px;\n      margin-top: 4px;\n      &:first-of-type {\n        margin-left: 15px;\n      }\n      &:last-of-type {\n        margin-right: 15px;\n      }\n      &.active {\n        background-color: #42b983;\n        color: #fff;\n        border-color: #42b983;\n        &::before {\n          content: '';\n          background: #fff;\n          display: inline-block;\n          width: 8px;\n          height: 8px;\n          border-radius: 50%;\n          position: relative;\n          margin-right: 2px;\n        }\n      }\n    }\n  }\n  .contextmenu {\n    margin: 0;\n    background: #fff;\n    z-index: 3000;\n    position: absolute;\n    list-style-type: none;\n    padding: 5px 0;\n    border-radius: 4px;\n    font-size: 12px;\n    font-weight: 400;\n    color: #333;\n    box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);\n    li {\n      margin: 0;\n      padding: 7px 16px;\n      cursor: pointer;\n      &:hover {\n        background: #eee;\n      }\n    }\n  }\n}\n</style>\n\n<style lang=\"scss\">\n//reset element css of el-icon-close\n.tags-view-wrapper {\n  .tags-view-item {\n    .el-icon-close {\n      width: 16px;\n      height: 16px;\n      vertical-align: 2px;\n      border-radius: 50%;\n      text-align: center;\n      transition: all .3s cubic-bezier(.645, .045, .355, 1);\n      transform-origin: 100% 50%;\n      &:before {\n        transform: scale(.6);\n        display: inline-block;\n        vertical-align: -3px;\n      }\n      &:hover {\n        background-color: #b4bccc;\n        color: #fff;\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/index.js",
    "content": "export { default as AppMain } from './AppMain'\nexport { default as Navbar } from './Navbar'\nexport { default as Settings } from './Settings'\nexport { default as Sidebar } from './Sidebar/index.vue'\nexport { default as TagsView } from './TagsView/index.vue'\n"
  },
  {
    "path": "frontend/src/layout/index.vue",
    "content": "<template>\n  <div :class=\"classObj\" class=\"app-wrapper\">\n    <div v-if=\"device==='mobile'&&sidebar.opened\" class=\"drawer-bg\" @click=\"handleClickOutside\" />\n    <sidebar class=\"sidebar-container\" />\n    <div :class=\"{hasTagsView:needTagsView}\" class=\"main-container\">\n      <div :class=\"{'fixed-header':fixedHeader}\">\n        <navbar />\n        <tags-view v-if=\"needTagsView\" />\n      </div>\n      <app-main />\n      <right-panel v-if=\"showSettings\">\n        <settings />\n      </right-panel>\n    </div>\n  </div>\n</template>\n\n<script>\nimport RightPanel from '@/components/RightPanel'\nimport { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'\nimport ResizeMixin from './mixin/ResizeHandler'\nimport { mapState } from 'vuex'\n\nexport default {\n  name: 'Layout',\n  components: {\n    AppMain,\n    Navbar,\n    RightPanel,\n    Settings,\n    Sidebar,\n    TagsView\n  },\n  mixins: [ResizeMixin],\n  computed: {\n    ...mapState({\n      sidebar: state => state.app.sidebar,\n      device: state => state.app.device,\n      showSettings: state => state.settings.showSettings,\n      needTagsView: state => state.settings.tagsView,\n      fixedHeader: state => state.settings.fixedHeader\n    }),\n    classObj() {\n      return {\n        hideSidebar: !this.sidebar.opened,\n        openSidebar: this.sidebar.opened,\n        withoutAnimation: this.sidebar.withoutAnimation,\n        mobile: this.device === 'mobile'\n      }\n    }\n  },\n  methods: {\n    handleClickOutside() {\n      this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n  @import \"~@/styles/mixin.scss\";\n  @import \"~@/styles/variables.scss\";\n\n  .app-wrapper {\n    @include clearfix;\n    position: relative;\n    height: 100%;\n    width: 100%;\n\n    &.mobile.openSidebar {\n      position: fixed;\n      top: 0;\n    }\n  }\n\n  .drawer-bg {\n    background: #000;\n    opacity: 0.3;\n    width: 100%;\n    top: 0;\n    height: 100%;\n    position: absolute;\n    z-index: 999;\n  }\n\n  .fixed-header {\n    position: fixed;\n    top: 0;\n    right: 0;\n    z-index: 9;\n    width: calc(100% - #{$sideBarWidth});\n    transition: width 0.28s;\n  }\n\n  .hideSidebar .fixed-header {\n    width: calc(100% - 54px)\n  }\n\n  .mobile .fixed-header {\n    width: 100%;\n  }\n</style>\n"
  },
  {
    "path": "frontend/src/layout/mixin/ResizeHandler.js",
    "content": "import store from '@/store'\n\nconst { body } = document\nconst WIDTH = 992 // refer to Bootstrap's responsive design\n\nexport default {\n  watch: {\n    $route(route) {\n      if (this.device === 'mobile' && this.sidebar.opened) {\n        store.dispatch('app/closeSideBar', { withoutAnimation: false })\n      }\n    }\n  },\n  beforeMount() {\n    window.addEventListener('resize', this.$_resizeHandler)\n  },\n  beforeDestroy() {\n    window.removeEventListener('resize', this.$_resizeHandler)\n  },\n  mounted() {\n    const isMobile = this.$_isMobile()\n    if (isMobile) {\n      store.dispatch('app/toggleDevice', 'mobile')\n      store.dispatch('app/closeSideBar', { withoutAnimation: true })\n    }\n  },\n  methods: {\n    // use $_ for mixins properties\n    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential\n    $_isMobile() {\n      const rect = body.getBoundingClientRect()\n      return rect.width - 1 < WIDTH\n    },\n    $_resizeHandler() {\n      if (!document.hidden) {\n        const isMobile = this.$_isMobile()\n        store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')\n\n        if (isMobile) {\n          store.dispatch('app/closeSideBar', { withoutAnimation: true })\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/src/main.js",
    "content": "import Vue from 'vue'\n\nimport Cookies from 'js-cookie'\n\nimport 'normalize.css/normalize.css' // a modern alternative to CSS resets\n\nimport Element from 'element-ui'\nimport './styles/element-variables.scss'\n\nimport '@/styles/index.scss' // global css\n\nimport App from './App'\nimport store from './store'\nimport router from './router'\n\nimport i18n from './lang' // Internationalization\nimport './icons' // icon\nimport './permission' // permission control\n\nimport * as filters from './filters' // global filters\n\nVue.use(Element, {\n  size: Cookies.get('size') || 'medium', // set element-ui default size\n  i18n: (key, value) => i18n.t(key, value)\n})\n\n// register global utility filters\nObject.keys(filters).forEach(key => {\n  Vue.filter(key, filters[key])\n})\n\nVue.config.productionTip = false\n\nnew Vue({\n  el: '#app',\n  router,\n  store,\n  i18n,\n  render: h => h(App)\n})\n"
  },
  {
    "path": "frontend/src/permission.js",
    "content": "import router from './router'\nimport store from './store'\nimport { Message } from 'element-ui'\nimport NProgress from 'nprogress' // progress bar\nimport 'nprogress/nprogress.css' // progress bar style\nimport { getToken } from '@/utils/auth' // get token from cookie\nimport getPageTitle from '@/utils/get-page-title'\nimport i18n from '@/lang'\n\nNProgress.configure({ showSpinner: false }) // NProgress Configuration\n\nconst whiteList = ['/login', '/auth-redirect'] // no redirect whitelist\n\nrouter.beforeEach(async(to, from, next) => {\n  // start progress bar\n  NProgress.start()\n\n  // set page title\n  const i18ntitle = i18n.t(`route.${to.meta.title}`)\n  document.title = getPageTitle(i18ntitle)\n\n  // determine whether the user has logged in\n  const hasToken = getToken()\n\n  if (hasToken) {\n    if (to.path === '/login') {\n      // if is logged in, redirect to the home page\n      next({ path: '/' })\n      NProgress.done()\n    } else {\n      // determine whether the user has obtained his permission roles through getInfo\n      const hasRoles = store.getters.roles && store.getters.roles.length > 0\n      if (hasRoles) {\n        next()\n      } else {\n        try {\n          // get user info\n          // note: roles must be a object array! such as: ['admin'] or ,['developer','editor']\n          const { menus } = await store.dispatch('user/getInfo')\n          // alert(roles)\n          // generate accessible routes map based on roles\n          const accessRoutes = await store.dispatch('permission/generateRoutes', menus)\n\n          // dynamically add accessible routes\n          router.addRoutes(accessRoutes)\n\n          // hack method to ensure that addRoutes is complete\n          // set the replace: true, so the navigation will not leave a history record\n          next({ ...to, replace: true })\n        } catch (error) {\n          // remove token and go to login page to re-login\n          await store.dispatch('user/resetToken')\n          Message.error(error || 'Has Error')\n          next(`/login?redirect=${to.path}`)\n          NProgress.done()\n        }\n      }\n    }\n  } else {\n    /* has no token*/\n\n    if (whiteList.indexOf(to.path) !== -1) {\n      // in the free login whitelist, go directly\n      next()\n    } else {\n      // other pages that do not have permission to access are redirected to the login page.\n      next(`/login?redirect=${to.path}`)\n      NProgress.done()\n    }\n  }\n})\n\nrouter.afterEach(() => {\n  // finish progress bar\n  NProgress.done()\n})\n"
  },
  {
    "path": "frontend/src/router/index.js",
    "content": "import Vue from 'vue'\nimport Router from 'vue-router'\n\nVue.use(Router)\n\n/* Layout */\nimport Layout from '@/layout'\n\n/* Router Modules */\n// import componentsRouter from './modules/components'\n\n/**\n * Note: sub-menu only appear when route children.length >= 1\n * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html\n *\n * hidden: true                   if set true, item will not show in the sidebar(default is false)\n * alwaysShow: true               if set true, will always show the root menu\n *                                if not set alwaysShow, when item has more than one children route,\n *                                it will becomes nested mode, otherwise not show the root menu\n * redirect: noRedirect           if set noRedirect will no redirect in the breadcrumb\n * name:'router-name'             the name is used by <keep-alive> (must set!!!)\n * meta : {\n    roles: ['admin','editor']    control the page roles (you can set multiple roles)\n    title: 'title'               the name show in sidebar and breadcrumb (recommend set)\n    icon: 'svg-name'             the icon show in the sidebar\n    noCache: true                if set true, the page will no be cached(default is false)\n    affix: true                  if set true, the tag will affix in the tags-view\n    breadcrumb: false            if set false, the item will hidden in breadcrumb(default is true)\n    activeMenu: '/example/list'  if set path, the sidebar will highlight the path you set\n  }\n */\n\n/**\n * constantRoutes\n * a base page that does not have permission requirements\n * all roles can be accessed\n */\nexport const constantRoutes = [\n  {\n    path: '/login',\n    component: () => import('@/views/login/index'),\n    hidden: true,\n    meta: { title: 'login' }\n  },\n  {\n    path: '/404',\n    component: () => import('@/views/error-page/404'),\n    hidden: true\n  },\n  {\n    path: '/401',\n    component: () => import('@/views/error-page/401'),\n    hidden: true\n  },\n  {\n    path: '/',\n    component: Layout,\n    redirect: '/dashboard',\n    children: [\n      {\n        path: 'dashboard',\n        component: () => import('@/views/dashboard/index'),\n        name: 'dashboard',\n        meta: { title: 'dashboard', icon: 'dashboard', affix: true }\n      }\n    ]\n  },\n  // componentsRouter\n]\n\nconst createRouter = () => new Router({\n  // mode: 'history', // require service support\n  scrollBehavior: () => ({ y: 0 }),\n  routes: constantRoutes\n})\n\nconst router = createRouter()\n\n// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465\nexport function resetRouter() {\n  const newRouter = createRouter()\n  router.matcher = newRouter.matcher // reset router\n}\n\nexport default router\n"
  },
  {
    "path": "frontend/src/router/modules/components.js",
    "content": "/** When your routing table is too long, you can split it into small modules **/\n\nimport Layout from '@/layout'\n\nconst componentsRouter = {\n  path: '/omponents-demo',\n  component: Layout,\n  redirect: 'noRedirect',\n  name: 'ComponentDemo',\n  meta: {\n    title: 'ComponentDemo',\n    icon: 'component'\n  },\n  children: [\n    {\n      path: 'clipboard',\n      component: () => import('@/views/components-demo/clipboard'),\n      name: 'clipboard',\n      meta: { title: 'clipboard' }\n    },\n    {\n      path: 'sticky',\n      component: () => import('@/views/components-demo/sticky'),\n      name: 'StickyDemo',\n      meta: { title: 'Sticky' }\n    },\n    {\n      path: 'mixin',\n      component: () => import('@/views/components-demo/mixin'),\n      name: 'ComponentMixinDemo',\n      meta: { title: 'Component Mixin' }\n    },\n    {\n      path: 'dnd-list',\n      component: () => import('@/views/components-demo/dnd-list'),\n      name: 'DndListDemo',\n      meta: { title: 'Dnd List' }\n    },\n    {\n      path: 'drag-kanban',\n      component: () => import('@/views/components-demo/drag-kanban'),\n      name: 'DragKanbanDemo',\n      meta: { title: 'Drag Kanban' }\n    }\n  ]\n}\n\nexport default componentsRouter\n"
  },
  {
    "path": "frontend/src/settings.js",
    "content": "// import i18n from '@/lang'\n\nmodule.exports = {\n  // title: i18n.t('systemTitle'),\n  title: '后台管理系统',\n\n  /**\n   * @type {boolean} true | false\n   * @description Whether show the settings right-panel\n   */\n  showSettings: true,\n\n  /**\n   * @type {boolean} true | false\n   * @description Whether need tagsView\n   */\n  tagsView: true,\n\n  /**\n   * @type {boolean} true | false\n   * @description Whether fix the header\n   */\n  fixedHeader: true,\n\n  /**\n   * @type {boolean} true | false\n   * @description Whether show the logo in sidebar\n   */\n  sidebarLogo: true,\n\n  /**\n   * @type {string | array} 'production' | ['production', 'development']\n   * @description Need show err logs component.\n   * The default is only used in the production env\n   * If you want to also use it in dev, you can pass ['production', 'development']\n   */\n  errorLog: 'production'\n}\n"
  },
  {
    "path": "frontend/src/store/getters.js",
    "content": "const getters = {\n  sidebar: state => state.app.sidebar,\n  language: state => state.app.language,\n  size: state => state.app.size,\n  device: state => state.app.device,\n  visitedViews: state => state.tagsView.visitedViews,\n  cachedViews: state => state.tagsView.cachedViews,\n  token: state => state.user.token,\n  avatar: state => state.user.avatar,\n  username: state => state.user.username,\n  user_id: state => state.user.user_id,\n  roles: state => state.user.roles,\n  ip: state => state.user.ip,\n  permission_routes: state => state.permission.routes,\n  addRoutes: state => state.permission.addRoutes\n}\nexport default getters"
  },
  {
    "path": "frontend/src/store/index.js",
    "content": "import Vue from 'vue'\nimport Vuex from 'vuex'\nimport getters from './getters'\n\nVue.use(Vuex)\n\n// https://webpack.js.org/guides/dependency-management/#requirecontext\nconst modulesFiles = require.context('./modules', true, /\\.js$/)\n\n// you do not need `import app from './modules/app'`\n// it will auto require all vuex module from modules file\nconst modules = modulesFiles.keys().reduce((modules, modulePath) => {\n  // set './app.js' => 'app'\n  const moduleName = modulePath.replace(/^\\.\\/(.*)\\.\\w+$/, '$1')\n  const value = modulesFiles(modulePath)\n  modules[moduleName] = value.default\n  return modules\n}, {})\n\nconst store = new Vuex.Store({\n  modules,\n  getters\n})\n\nexport default store\n"
  },
  {
    "path": "frontend/src/store/modules/app.js",
    "content": "import Cookies from 'js-cookie'\nimport { getLanguage } from '@/lang/index'\n\nconst state = {\n  sidebar: {\n    opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,\n    withoutAnimation: false\n  },\n  device: 'desktop',\n  language: getLanguage(),\n  size: Cookies.get('size') || 'medium'\n}\n\nconst mutations = {\n  TOGGLE_SIDEBAR: state => {\n    state.sidebar.opened = !state.sidebar.opened\n    state.sidebar.withoutAnimation = false\n    if (state.sidebar.opened) {\n      Cookies.set('sidebarStatus', 1)\n    } else {\n      Cookies.set('sidebarStatus', 0)\n    }\n  },\n  CLOSE_SIDEBAR: (state, withoutAnimation) => {\n    Cookies.set('sidebarStatus', 0)\n    state.sidebar.opened = false\n    state.sidebar.withoutAnimation = withoutAnimation\n  },\n  TOGGLE_DEVICE: (state, device) => {\n    state.device = device\n  },\n  SET_LANGUAGE: (state, language) => {\n    state.language = language\n    Cookies.set('language', language)\n  },\n  SET_SIZE: (state, size) => {\n    state.size = size\n    Cookies.set('size', size)\n  }\n}\n\nconst actions = {\n  toggleSideBar({ commit }) {\n    commit('TOGGLE_SIDEBAR')\n  },\n  closeSideBar({ commit }, { withoutAnimation }) {\n    commit('CLOSE_SIDEBAR', withoutAnimation)\n  },\n  toggleDevice({ commit }, device) {\n    commit('TOGGLE_DEVICE', device)\n  },\n  setLanguage({ commit }, language) {\n    commit('SET_LANGUAGE', language)\n  },\n  setSize({ commit }, size) {\n    commit('SET_SIZE', size)\n  }\n}\n\nexport default {\n  namespaced: true,\n  state,\n  mutations,\n  actions\n}\n"
  },
  {
    "path": "frontend/src/store/modules/permission.js",
    "content": "import { asyncRoutes, constantRoutes } from '@/router'\n// import { getMenus } from '@/api/user'\n/* Layout */\nimport Layout from '@/layout'\n// import { getToken } from '@/utils/auth'\n/**\n * 通过meta.role判断是否与当前用户权限匹配\n * @param roles\n * @param route\n */\nfunction hasPermission(roles, route) {\n  if (route.meta && route.meta.roles) {\n    return roles.some(role => route.meta.roles.includes(role))\n  } else {\n    return true\n  }\n}\n\n/**\n * 递归过滤异步路由表，返回符合用户角色权限的路由表\n * @param routes asyncRoutes\n * @param roles\n */\nexport function filterAsyncRoutes(routes, roles) {\n  const res = []\n\n  routes.forEach(route => {\n    const tmp = { ...route }\n    if (hasPermission(roles, tmp)) {\n      if (tmp.children) {\n        tmp.children = filterAsyncRoutes(tmp.children, roles)\n      }\n      res.push(tmp)\n    }\n  })\n\n  return res\n}\n\n// 在这里定义state 状态管理\nconst state = {\n  routes: [],\n  addRoutes: []\n}\n\nconst mutations = {\n  SET_ROUTES: (state, routes) => {\n    state.addRoutes = routes\n    state.routes = constantRoutes.concat(routes)\n  }\n}\n\nconst actions = {\n  generateRoutes2({ commit }, roles) {\n    return new Promise(resolve => {\n      let accessedRoutes\n      if (roles.includes('admin')) {\n        // 如果是管理员则加载所有路由\n        accessedRoutes = asyncRoutes\n      } else {\n        // 如果不是总管理员则进行过滤\n        // accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)\n        return\n        /**\n        // 从服务端请求菜单列表数据\n        getMenus(state.username).then(response => {\n          const { code, menus } = response // 这种形式可以直接接收 {code:2000,data:\"ok\"}\n          console.log(code)\n          console.log(menus)\n          // const { roles, name, avatar, introduction } = data\n          // 处理路由、菜单\n          // const asyncRouterMap_copy = []\n          // accessedRoutes = asyncRouterMap_copy\n        }).catch(error => {\n          console.log(error)\n          return\n        })\n        **/\n        // end\n      }\n      commit('SET_ROUTES', accessedRoutes)\n      resolve(accessedRoutes)\n    })\n  },\n  generateRoutes({ commit }, data) {\n    return new Promise(resolve => {\n      const accessedRouters = convertRouter(data)\n      commit('SET_ROUTES', accessedRouters)\n      resolve(accessedRouters)\n    })\n  }\n}\n\nexport default {\n  namespaced: true,\n  state,\n  mutations,\n  actions\n}\n\n/**\n *将后台的路由表进行格式化\n * @param {*} asyncRouterMap\n */\nfunction convertRouter(asyncRouterMap) {\n  const accessedRouters = []\n  if (asyncRouterMap) {\n    asyncRouterMap.forEach(item => {\n      var isParent = false\n      if (item.children) {\n        isParent = true\n      }\n      var parent = generateRouter(item, isParent)\n      // console.log('003')\n      // console.log(parent)\n      var children = []\n      if (item.children) {\n        // console.log('004')\n        item.children.forEach(child => {\n          // 下一级\n          var children2 = []\n          if (child.children) {\n            child.children.forEach(child2 => {\n              children2.push(generateRouter(child2, false))\n            })\n          }\n          var parent2 = generateRouter(child, false)\n          parent2.children = children2\n          children.push(parent2)\n          // 下一级 end\n          // children.push(generateRouter(child, false))\n        })\n      }\n      parent.children = children\n      accessedRouters.push(parent)\n    })\n  }\n  accessedRouters.push({ path: '*', redirect: '/404', hidden: true })\n  return accessedRouters\n}\n\n// 对component的处理\nfunction generateRouter(item, isParent) {\n  var component = Layout // 多层嵌套时只能有一个Layout\n  if (isParent !== true) {\n    component = componentsMap[item.component]\n  }\n  var router = {\n    path: item.path,\n    name: item.name,\n    meta: item.meta,\n    noCache: item.no_cache,\n    activeMenu: item.active_menu,\n    hidden: item.hidden,\n    // component: isParent ? Layout : () => import(item.component) // 这个不可以\n    // component: isParent ? Layout : componentsMap[item.component]\n    component: component\n  }\n  if (isParent !== true) {\n    // router.meta = item.meta\n    // router.name = item.name\n  }\n  // router.meta.noCache = true\n  return router\n}\n\n// componentsMap 需要在事先定义好\nexport const componentsMap = {\n  // sys\n  menu: () => import('@/views/sys/menu'), // 菜单\n  group: () => import('@/views/sys/group'), // 用户组\n  user: () => import('@/views/sys/user'), // 用户\n  role: () => import('@/views/sys/role'), // 角色\n  icon: () => import('@/views/icons/index'), // 图标管理\n\n  // tool\n  audit: () => import('@/views/tool/audit'), // 审计日志\n  test: () => import('@/views/tool/test'), // test\n\n  // workflow\n  wfset: () => import('@/views/workflow/wfset'), // 工作流设计\n  wfconf: () => import('@/views/workflow/wfconf'), // 工作流配置\n  wftype: () => import('@/views/workflow/wftype'), // 工单类型\n\n  // tickets\n  new_ticket: () => import('@/views/ticket/new_ticket'), // 新建工单\n  u_ticket: () => import('@/views/ticket/u_ticket'), // 编辑工单\n  s_ticket: () => import('@/views/ticket/s_ticket'), // 审批工单\n  my_ticket: () => import('@/views/ticket/my_ticket'), // 我创建的\n  todo_ticket: () => import('@/views/ticket/todo_ticket'), // 我的待办\n  all_ticket: () => import('@/views/ticket/all_ticket'), // 编辑工单\n\n  // notice\n  mail: () => import('@/views/notice/mail'), // mail bot\n  telegram: () => import('@/views/notice/telegram'), // telegram bot\n}\n"
  },
  {
    "path": "frontend/src/store/modules/settings.js",
    "content": "import variables from '@/styles/element-variables.scss'\nimport defaultSettings from '@/settings'\n\nconst { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings\n\nconst state = {\n  theme: variables.theme,\n  showSettings: showSettings,\n  tagsView: tagsView,\n  fixedHeader: fixedHeader,\n  sidebarLogo: sidebarLogo\n}\n\nconst mutations = {\n  CHANGE_SETTING: (state, { key, value }) => {\n    if (state.hasOwnProperty(key)) {\n      state[key] = value\n    }\n  }\n}\n\nconst actions = {\n  changeSetting({ commit }, data) {\n    commit('CHANGE_SETTING', data)\n  }\n}\n\nexport default {\n  namespaced: true,\n  state,\n  mutations,\n  actions\n}\n\n"
  },
  {
    "path": "frontend/src/store/modules/tagsView.js",
    "content": "const state = {\n  visitedViews: [],\n  cachedViews: []\n}\n\nconst mutations = {\n  ADD_VISITED_VIEW: (state, view) => {\n    if (state.visitedViews.some(v => v.path === view.path)) return\n      if (!view.meta.hidden) {\n        state.visitedViews.push(\n          Object.assign({}, view, {\n            title: view.meta.title || 'no-name'\n          })\n        )\n      }\n  },\n  ADD_CACHED_VIEW: (state, view) => {\n    if (state.cachedViews.includes(view.name)) return\n    if (!view.meta.noCache & !view.meta.hidden) {\n      state.cachedViews.push(view.name)\n    }\n  },\n\n  DEL_VISITED_VIEW: (state, view) => {\n    for (const [i, v] of state.visitedViews.entries()) {\n      if (v.path === view.path) {\n        state.visitedViews.splice(i, 1)\n        break\n      }\n    }\n  },\n  DEL_CACHED_VIEW: (state, view) => {\n    const index = state.cachedViews.indexOf(view.name)\n    index > -1 && state.cachedViews.splice(index, 1)\n  },\n\n  DEL_OTHERS_VISITED_VIEWS: (state, view) => {\n    state.visitedViews = state.visitedViews.filter(v => {\n      return v.meta.affix || v.path === view.path\n    })\n  },\n  DEL_OTHERS_CACHED_VIEWS: (state, view) => {\n    const index = state.cachedViews.indexOf(view.name)\n    if (index > -1) {\n      state.cachedViews = state.cachedViews.slice(index, index + 1)\n    } else {\n      // if index = -1, there is no cached tags\n      state.cachedViews = []\n    }\n  },\n\n  DEL_ALL_VISITED_VIEWS: state => {\n    // keep affix tags\n    const affixTags = state.visitedViews.filter(tag => tag.meta.affix)\n    state.visitedViews = affixTags\n  },\n  DEL_ALL_CACHED_VIEWS: state => {\n    state.cachedViews = []\n  },\n\n  UPDATE_VISITED_VIEW: (state, view) => {\n    for (let v of state.visitedViews) {\n      if (v.path === view.path) {\n        v = Object.assign(v, view)\n        break\n      }\n    }\n  }\n}\n\nconst actions = {\n  addView({ dispatch }, view) {\n    dispatch('addVisitedView', view)\n    dispatch('addCachedView', view)\n  },\n  addVisitedView({ commit }, view) {\n    commit('ADD_VISITED_VIEW', view)\n  },\n  addCachedView({ commit }, view) {\n    commit('ADD_CACHED_VIEW', view)\n  },\n\n  delView({ dispatch, state }, view) {\n    return new Promise(resolve => {\n      dispatch('delVisitedView', view)\n      dispatch('delCachedView', view)\n      resolve({\n        visitedViews: [...state.visitedViews],\n        cachedViews: [...state.cachedViews]\n      })\n    })\n  },\n  delVisitedView({ commit, state }, view) {\n    return new Promise(resolve => {\n      commit('DEL_VISITED_VIEW', view)\n      resolve([...state.visitedViews])\n    })\n  },\n  delCachedView({ commit, state }, view) {\n    return new Promise(resolve => {\n      commit('DEL_CACHED_VIEW', view)\n      resolve([...state.cachedViews])\n    })\n  },\n\n  delOthersViews({ dispatch, state }, view) {\n    return new Promise(resolve => {\n      dispatch('delOthersVisitedViews', view)\n      dispatch('delOthersCachedViews', view)\n      resolve({\n        visitedViews: [...state.visitedViews],\n        cachedViews: [...state.cachedViews]\n      })\n    })\n  },\n  delOthersVisitedViews({ commit, state }, view) {\n    return new Promise(resolve => {\n      commit('DEL_OTHERS_VISITED_VIEWS', view)\n      resolve([...state.visitedViews])\n    })\n  },\n  delOthersCachedViews({ commit, state }, view) {\n    return new Promise(resolve => {\n      commit('DEL_OTHERS_CACHED_VIEWS', view)\n      resolve([...state.cachedViews])\n    })\n  },\n\n  delAllViews({ dispatch, state }, view) {\n    return new Promise(resolve => {\n      dispatch('delAllVisitedViews', view)\n      dispatch('delAllCachedViews', view)\n      resolve({\n        visitedViews: [...state.visitedViews],\n        cachedViews: [...state.cachedViews]\n      })\n    })\n  },\n  delAllVisitedViews({ commit, state }) {\n    return new Promise(resolve => {\n      commit('DEL_ALL_VISITED_VIEWS')\n      resolve([...state.visitedViews])\n    })\n  },\n  delAllCachedViews({ commit, state }) {\n    return new Promise(resolve => {\n      commit('DEL_ALL_CACHED_VIEWS')\n      resolve([...state.cachedViews])\n    })\n  },\n\n  updateVisitedView({ commit }, view) {\n    commit('UPDATE_VISITED_VIEW', view)\n  }\n}\n\nexport default {\n  namespaced: true,\n  state,\n  mutations,\n  actions\n}\n"
  },
  {
    "path": "frontend/src/store/modules/user.js",
    "content": "import { auth } from '@/api/all'\nimport { getToken, setToken, removeToken } from '@/utils/auth'\nimport { resetRouter } from '@/router'\n\nconst state = {\n  token: getToken(),\n  username: '',\n  user_id: '',\n  avatar: '',\n  roles: [],\n  ip: ''\n}\n\nconst mutations = {\n  SET_TOKEN: (state, token) => {\n    state.token = token\n  },\n  SET_USER_ID: (state, user_id) => {\n    state.user_id = user_id\n  },\n  SET_USERNAME: (state, username) => {\n    state.username = username\n  },\n  SET_AVATAR: (state, avatar) => {\n    state.avatar = avatar\n  },\n  SET_ROLES: (state, roles) => {\n    state.roles = roles\n  },\n  SET_IP: (state, ip) => {\n    state.ip = ip\n  }\n}\n\nconst actions = {\n  // user login\n  login({ commit }, userInfo) {\n    const { username, password } = userInfo\n    return new Promise((resolve, reject) => {\n      auth.login({ username: username.trim(), password: password }).then(response => {\n        const data = response.results\n        commit('SET_TOKEN', data.token)\n        setToken(data.token)\n        resolve()\n      }).catch(error => {\n        console.log(error)\n        reject(error)\n      })\n    })\n  },\n\n  // get user info\n  getInfo({ commit, state }) {\n    return new Promise((resolve, reject) => {\n      auth.getInfo(state.token).then(response => {\n        const data = response.results\n        if (!data) {\n          reject('Verification failed, please Login again.')\n        }\n        const { username, avatar, user_id, ip } = data\n\n        // roles must be a non-empty array\n        // if (!roles || roles.length <= 0) {\n        // reject('getInfo: roles must be a non-null array!')\n        // }\n        const roles = ['admin']\n        commit('SET_ROLES', roles)\n        commit('SET_USERNAME', username)\n        commit('SET_AVATAR', avatar)\n        commit('SET_USER_ID', user_id)\n        commit('SET_IP', ip)\n        resolve(data)\n      }).catch(error => {\n        reject(error)\n      })\n    })\n  },\n\n  // user logout\n  logout({ commit, state }) {\n    return new Promise((resolve, reject) => {\n        commit('SET_TOKEN', '')\n        commit('SET_ROLES', [])\n        removeToken()\n        resetRouter()\n        resolve()\n    })\n  },\n\n  // remove token\n  resetToken({ commit }) {\n    return new Promise(resolve => {\n      commit('SET_TOKEN', '')\n      commit('SET_ROLES', [])\n      removeToken()\n      resolve()\n    })\n  }\n}\n\nexport default {\n  namespaced: true,\n  state,\n  mutations,\n  actions\n}"
  },
  {
    "path": "frontend/src/styles/btn.scss",
    "content": "@import './variables.scss';\n\n@mixin colorBtn($color) {\n  background: $color;\n\n  &:hover {\n    color: $color;\n\n    &:before,\n    &:after {\n      background: $color;\n    }\n  }\n}\n\n.blue-btn {\n  @include colorBtn($blue)\n}\n\n.light-blue-btn {\n  @include colorBtn($light-blue)\n}\n\n.red-btn {\n  @include colorBtn($red)\n}\n\n.pink-btn {\n  @include colorBtn($pink)\n}\n\n.green-btn {\n  @include colorBtn($green)\n}\n\n.tiffany-btn {\n  @include colorBtn($tiffany)\n}\n\n.yellow-btn {\n  @include colorBtn($yellow)\n}\n\n.pan-btn {\n  font-size: 14px;\n  color: #fff;\n  padding: 14px 36px;\n  border-radius: 8px;\n  border: none;\n  outline: none;\n  transition: 600ms ease all;\n  position: relative;\n  display: inline-block;\n  width: 100%;\n  text-align: center;\n\n  &:hover {\n    background: #fff;\n\n    &:before,\n    &:after {\n      width: 100%;\n      transition: 600ms ease all;\n    }\n  }\n\n  &:before,\n  &:after {\n    content: '';\n    position: absolute;\n    top: 0;\n    right: 0;\n    height: 2px;\n    width: 0;\n    transition: 400ms ease all;\n  }\n\n  &::after {\n    right: inherit;\n    top: inherit;\n    left: 0;\n    bottom: 0;\n  }\n}\n\n.custom-button {\n  display: inline-block;\n  line-height: 1;\n  white-space: nowrap;\n  cursor: pointer;\n  background: #fff;\n  color: #fff;\n  -webkit-appearance: none;\n  text-align: center;\n  box-sizing: border-box;\n  outline: 0;\n  margin: 0;\n  padding: 10px 15px;\n  font-size: 14px;\n  border-radius: 4px;\n}\n"
  },
  {
    "path": "frontend/src/styles/csshake.scss",
    "content": "// Variables\n$prefix: 'shake' !default;\n$trigger: 'shake-trigger' !default;\n\n// Placeholders\n%shake {\n  display: inherit;\n  transform-origin: center center;\n}\n\n%paused   { animation-play-state: paused; }\n%running  { animation-play-state: running; }\n\n@function apply-random($input) {\n  @return if($input != 0, random($input) - $input/2, 0);\n}\n\n/// Do The Shake\n/// @param {String}  $name ['shake']              - Name for the keyframes animation\n/// @param {Number}  $h [5px]                     - Max number for random to assign in x axis\n/// @param {Number}  $v [5px]                     - Max number for random to assign in y axis\n/// @param {Number}  $r [3deg]                    - Max number for random rotation\n/// @param {Number}  $dur [100ms]                 - animation-duration; valid time value\n/// @param {Number}  $precision [.1]              - Precision of the keyframes animation (i.e 2 > 2%, 4%, 6%...)\n/// @param {Boolean} $opacity [false]             - To apply random animation also in the opacity property\n/// @param {String}  $q [infinite]                - animation-iteration-count; valid value\n/// @param {String}  $t [ease-in-out]             - animation-timing-function; valid value\n/// @param {Number}  $delay [null]                - animation-delay; valid time value\n/// @param {Number}  $chunk [100%]                - Part of 100% where apply the animation\n@mixin do-shake(\n  $name: 'shake', \n  $h: 5px, \n  $v: 5px, \n  $r: 3deg, \n  $dur: 100ms, \n  $precision: .02, \n  $opacity: false, \n  $q: infinite, \n  $t: ease-in-out, \n  $delay: null,\n  $chunk: 100%\n  ) {  \n\n  $rotate: 0;\n  $move-x: 0;\n  $move-y: 0;\n\n  $h: if(unitless($h) and $h != 0, $h * 1px, $h);\n  $v: if(unitless($v) and $v != 0, $v * 1px, $v);\n  $r: if(unitless($r) and $r != 0, $r * 1deg, $r);\n\n  // Keyframes\n  @at-root {\n    @keyframes #{$name} {\n      $interval: if($precision > 0 and $precision < 1, 100 * $precision, 10);\n      $step: $interval * 1%;\n\n      @while $step < $chunk {\n        $rotate: apply-random($r);\n        $move-x: apply-random($h);\n        $move-y: apply-random($v);\n        \n        #{$step} {\n          transform: translate($move-x, $move-y) rotate($rotate);\n          @if $opacity { opacity: random(100) / 100; }\n        }\n        \n        $step: $step + $interval;\n      }\n\n      #{ if($chunk < 100%, (0%, $chunk, 100%), (0%, 100%)) } {\n        transform: translate(0, 0) rotate(0);\n      }\n\n    }\n  }\n\n  @extend %shake;\n\n  &:hover,\n  .#{$trigger}:hover &,\n  &.#{$prefix}-freeze,\n  &.#{$prefix}-constant {\n    @if $delay { animation-delay: $delay; }\n    animation-name: #{$name};\n    animation-duration: $dur;\n    animation-timing-function: $t;\n    animation-iteration-count: $q;\n  }\n\n  &:hover,\n  .#{$trigger}:hover & { @extend %running; }\n\n}\n\n.#{$prefix}-freeze,\n.#{$prefix}-constant.#{$prefix}-constant--hover:hover,\n.#{$trigger}:hover .#{$prefix}-constant.#{$prefix}-constant--hover {\n  @extend %paused;\n}\n\n.#{$prefix}-freeze:hover,\n.#{$trigger}:hover .#{$prefix}-freeze { @extend %running; }\n\n.shake-crazy { @include do-shake('shake-crazy', 0, 20, 0); }\n"
  },
  {
    "path": "frontend/src/styles/element-ui.scss",
    "content": "// cover some element-ui styles\n\n.el-breadcrumb__inner,\n.el-breadcrumb__inner a {\n  font-weight: 400 !important;\n}\n\n.el-upload {\n  input[type=\"file\"] {\n    display: none !important;\n  }\n}\n\n.el-upload__input {\n  display: none;\n}\n\n.cell {\n  .el-tag {\n    margin-right: 0px;\n  }\n}\n\n.small-padding {\n  .cell {\n    padding-left: 5px;\n    padding-right: 5px;\n  }\n}\n\n.fixed-width {\n  .el-button--mini {\n    padding: 7px 10px;\n    width: 60px;\n  }\n}\n\n.status-col {\n  .cell {\n    padding: 0 10px;\n    text-align: center;\n\n    .el-tag {\n      margin-right: 0px;\n    }\n  }\n}\n\n// to fixed https://github.com/ElemeFE/element/issues/2461\n.el-dialog {\n  transform: none;\n  left: 0;\n  position: relative;\n  margin: 0 auto;\n}\n\n// refine element ui upload\n.upload-container {\n  .el-upload {\n    width: 100%;\n\n    .el-upload-dragger {\n      width: 100%;\n      height: 200px;\n    }\n  }\n}\n\n// dropdown\n.el-dropdown-menu {\n  a {\n    display: block\n  }\n}\n\n// fix date-picker ui bug in filter-item\n.el-range-editor.el-input__inner {\n  display: inline-flex !important;\n}\n\n// to fix el-date-picker css style\n.el-range-separator {\n  box-sizing: content-box;\n}\n\n.el-transfer__buttons {\n  padding: 0 5px!important;\n}\n\n.card-solt {\n  width: 450px;\n}\n\n.card-span-title {\n  font-family: \"STXingkai\", sans-serif;\n  font-weight: 900;\n  font-size: 1.8em;\n}\n\n.el-card {\n  margin-bottom: 10px;\n  .card-title {\n    font-weight: 700;\n  }\n  .el-card__body{\n    padding: 5px 20px;\n  }\n}"
  },
  {
    "path": "frontend/src/styles/element-variables.scss",
    "content": "/**\n* I think element-ui's default theme color is too light for long-term use.\n* So I modified the default color and you can modify it to your liking.\n**/\n\n/* theme color */\n$--color-primary: #1890ff;\n$--color-success: #13ce66;\n$--color-warning: #FFBA00;\n$--color-danger: #ff4949;\n$--color-info: #1E1E1E;\n\n$--button-font-weight: 400;\n\n$--color-text-regular: #1f2d3d;\n\n$--border-color-light: #dfe4ed;\n$--border-color-lighter: #e6ebf5;\n\n$--table-border:1px solid#dfe6ec;\n\n/* icon font path, required */\n$--font-path: '~element-ui/lib/theme-chalk/fonts';\n\n@import \"~element-ui/packages/theme-chalk/src/index\";\n\n// the :export directive is the magic sauce for webpack\n// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass\n:export {\n  theme: $--color-primary;\n}\n"
  },
  {
    "path": "frontend/src/styles/index.scss",
    "content": "@import \"./variables.scss\";\n@import \"./mixin.scss\";\n@import \"./transition.scss\";\n@import \"./element-ui.scss\";\n@import \"./sidebar.scss\";\n@import \"./btn.scss\";\n@import \"./csshake.scss\";\n\nbody {\n  height: 100%;\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-font-smoothing: antialiased;\n  text-rendering: optimizeLegibility;\n  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,\n    Microsoft YaHei, Arial, sans-serif;\n}\n\nlabel {\n  font-weight: 700;\n}\n\nhtml {\n  height: 100%;\n  box-sizing: border-box;\n}\n\n#app {\n  height: 100%;\n}\n\n*,\n*:before,\n*:after {\n  box-sizing: inherit;\n}\n\n.no-padding {\n  padding: 0px !important;\n}\n\n.padding-content {\n  padding: 4px 0;\n}\n\na:focus,\na:active {\n  outline: none;\n}\n\na,\na:focus,\na:hover {\n  cursor: pointer;\n  color: inherit;\n  text-decoration: none;\n}\n\ndiv:focus {\n  outline: none;\n}\n\n.fr {\n  float: right;\n}\n\n.fl {\n  float: left;\n}\n\n.pr-5 {\n  padding-right: 5px;\n}\n\n.pl-5 {\n  padding-left: 5px;\n}\n\n.block {\n  display: block;\n}\n\n.pointer {\n  cursor: pointer;\n}\n\n.inlineBlock {\n  display: block;\n}\n\n.clearfix {\n  &:after {\n    visibility: hidden;\n    display: block;\n    font-size: 0;\n    content: \" \";\n    clear: both;\n    height: 0;\n  }\n}\n\ncode {\n  background: #eef1f6;\n  padding: 15px 16px;\n  margin-bottom: 20px;\n  display: block;\n  line-height: 36px;\n  font-size: 15px;\n  font-family: \"Source Sans Pro\", \"Helvetica Neue\", Arial, sans-serif;\n\n  a {\n    color: #337ab7;\n    cursor: pointer;\n\n    &:hover {\n      color: rgb(32, 160, 255);\n    }\n  }\n}\n\n.warn-content {\n  background: rgba(66, 185, 131, 0.1);\n  border-radius: 2px;\n  padding: 16px;\n  padding: 1rem;\n  line-height: 1.6rem;\n  word-spacing: 0.05rem;\n\n  a {\n    color: #42b983;\n    font-weight: 600;\n  }\n}\n\n//main-container全局样式\n.app-container {\n  padding: 20px;\n}\n\n.components-container {\n  margin: 30px 50px;\n  position: relative;\n}\n\n.pagination-container {\n  margin-top: 30px;\n}\n\n.text-center {\n  text-align: center;\n}\n\n.sub-navbar {\n  height: 50px;\n  line-height: 50px;\n  position: relative;\n  width: 100%;\n  text-align: right;\n  padding-right: 20px;\n  transition: 600ms ease position;\n  background: linear-gradient(90deg,\n      rgba(32, 182, 249, 1) 0%,\n      rgba(32, 182, 249, 1) 0%,\n      rgba(33, 120, 241, 1) 100%,\n      rgba(33, 120, 241, 1) 100%);\n\n  .subtitle {\n    font-size: 20px;\n    color: #fff;\n  }\n\n  &.draft {\n    background: #d0d0d0;\n  }\n\n  &.deleted {\n    background: #d0d0d0;\n  }\n}\n\n.link-type,\n.link-type:focus {\n  color: #337ab7;\n  cursor: pointer;\n\n  &:hover {\n    color: rgb(32, 160, 255);\n  }\n}\n\n.filter-container {\n  padding-bottom: 10px;\n\n  .filter-item {\n    display: inline-block;\n    vertical-align: middle;\n    margin-bottom: 10px;\n  }\n}\n\n//refine vue-multiselect plugin\n.multiselect {\n  line-height: 16px;\n}\n\n.multiselect--active {\n  z-index: 1000 !important;\n}\n\n.table-pagination {\n  padding: 10px 0;\n  float: right;\n\n  .pagination-container {\n    margin: 0;\n    padding: 10px 0 !important;\n  }\n}\n\np img {\n  max-width: 100%;\n}\n\n.tips {\n  color: rgba(128, 128, 128, 0.82);\n  font-size: 12px;\n  line-height: 70%;\n}\n\n.ticket-form {\n  width: 900px;\n  margin: 0 auto;\n}\n"
  },
  {
    "path": "frontend/src/styles/mixin.scss",
    "content": "@mixin clearfix {\n  &:after {\n    content: \"\";\n    display: table;\n    clear: both;\n  }\n}\n\n@mixin scrollBar {\n  &::-webkit-scrollbar-track-piece {\n    background: #d3dce6;\n  }\n\n  &::-webkit-scrollbar {\n    width: 6px;\n  }\n\n  &::-webkit-scrollbar-thumb {\n    background: #99a9bf;\n    border-radius: 20px;\n  }\n}\n\n@mixin relative {\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n\n@mixin pct($pct) {\n  width: #{$pct};\n  position: relative;\n  margin: 0 auto;\n}\n\n@mixin triangle($width, $height, $color, $direction) {\n  $width: $width/2;\n  $color-border-style: $height solid $color;\n  $transparent-border-style: $width solid transparent;\n  height: 0;\n  width: 0;\n\n  @if $direction==up {\n    border-bottom: $color-border-style;\n    border-left: $transparent-border-style;\n    border-right: $transparent-border-style;\n  }\n\n  @else if $direction==right {\n    border-left: $color-border-style;\n    border-top: $transparent-border-style;\n    border-bottom: $transparent-border-style;\n  }\n\n  @else if $direction==down {\n    border-top: $color-border-style;\n    border-left: $transparent-border-style;\n    border-right: $transparent-border-style;\n  }\n\n  @else if $direction==left {\n    border-right: $color-border-style;\n    border-top: $transparent-border-style;\n    border-bottom: $transparent-border-style;\n  }\n}\n"
  },
  {
    "path": "frontend/src/styles/sidebar.scss",
    "content": "#app {\n\n  .main-container {\n    min-height: 100%;\n    transition: margin-left .28s;\n    margin-left: $sideBarWidth;\n    position: relative;\n  }\n\n  .sidebar-container {\n    transition: width 0.28s;\n    width: $sideBarWidth !important;\n    background-color: $menuBg;\n    height: 100%;\n    position: fixed;\n    font-size: 0px;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    z-index: 1001;\n    overflow: hidden;\n\n    // reset element-ui css\n    .horizontal-collapse-transition {\n      transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;\n    }\n\n    .scrollbar-wrapper {\n      overflow-x: hidden !important;\n    }\n\n    .el-scrollbar__bar.is-vertical {\n      right: 0px;\n    }\n\n    .el-scrollbar {\n      height: 100%;\n    }\n\n    &.has-logo {\n      .el-scrollbar {\n        height: calc(100% - 50px);\n      }\n    }\n\n    .is-horizontal {\n      display: none;\n    }\n\n    a {\n      display: inline-block;\n      width: 100%;\n      overflow: hidden;\n    }\n\n    .svg-icon {\n      margin-right: 16px;\n    }\n\n    .el-menu {\n      border: none;\n      height: 100%;\n      width: 100% !important;\n    }\n\n    // menu hover\n    .submenu-title-noDropdown,\n    .el-submenu__title {\n      &:hover {\n        background-color: $menuHover !important;\n      }\n    }\n\n    .is-active>.el-submenu__title {\n      color: $subMenuActiveText !important;\n    }\n\n    & .nest-menu .el-submenu>.el-submenu__title,\n    & .el-submenu .el-menu-item {\n      min-width: $sideBarWidth !important;\n      background-color: $subMenuBg !important;\n\n      &:hover {\n        background-color: $subMenuHover !important;\n      }\n    }\n  }\n\n  .hideSidebar {\n    .sidebar-container {\n      width: 54px !important;\n    }\n\n    .main-container {\n      margin-left: 54px;\n    }\n\n    .submenu-title-noDropdown {\n      padding: 0 !important;\n      position: relative;\n\n      .el-tooltip {\n        padding: 0 !important;\n\n        .svg-icon {\n          margin-left: 20px;\n        }\n      }\n    }\n\n    .el-submenu {\n      overflow: hidden;\n\n      &>.el-submenu__title {\n        padding: 0 !important;\n\n        .svg-icon {\n          margin-left: 20px;\n        }\n\n        .el-submenu__icon-arrow {\n          display: none;\n        }\n      }\n    }\n\n    .el-menu--collapse {\n      .el-submenu {\n        &>.el-submenu__title {\n          &>span {\n            height: 0;\n            width: 0;\n            overflow: hidden;\n            visibility: hidden;\n            display: inline-block;\n          }\n        }\n      }\n    }\n  }\n\n  .el-menu--collapse .el-menu .el-submenu {\n    min-width: $sideBarWidth !important;\n  }\n\n  // mobile responsive\n  .mobile {\n    .main-container {\n      margin-left: 0px;\n    }\n\n    .sidebar-container {\n      transition: transform .28s;\n      width: $sideBarWidth !important;\n    }\n\n    &.hideSidebar {\n      .sidebar-container {\n        pointer-events: none;\n        transition-duration: 0.3s;\n        transform: translate3d(-$sideBarWidth, 0, 0);\n      }\n    }\n  }\n\n  .withoutAnimation {\n\n    .main-container,\n    .sidebar-container {\n      transition: none;\n    }\n  }\n}\n\n// when menu collapsed\n.el-menu--vertical {\n  &>.el-menu {\n    .svg-icon {\n      margin-right: 16px;\n    }\n  }\n\n  .nest-menu .el-submenu>.el-submenu__title,\n  .el-menu-item {\n    &:hover {\n      // you can use $subMenuHover\n      background-color: $menuHover !important;\n    }\n  }\n\n  // the scroll bar appears when the subMenu is too long\n  >.el-menu--popup {\n    max-height: 100vh;\n    overflow-y: auto;\n\n    &::-webkit-scrollbar-track-piece {\n      background: #d3dce6;\n    }\n\n    &::-webkit-scrollbar {\n      width: 6px;\n    }\n\n    &::-webkit-scrollbar-thumb {\n      background: #99a9bf;\n      border-radius: 20px;\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/src/styles/transition.scss",
    "content": "// global transition css\n\n/* fade */\n.fade-enter-active,\n.fade-leave-active {\n  transition: opacity 0.28s;\n}\n\n.fade-enter,\n.fade-leave-active {\n  opacity: 0;\n}\n\n/* fade-transform */\n.fade-transform-leave-active,\n.fade-transform-enter-active {\n  transition: all .5s;\n}\n\n.fade-transform-enter {\n  opacity: 0;\n  transform: translateX(-30px);\n}\n\n.fade-transform-leave-to {\n  opacity: 0;\n  transform: translateX(30px);\n}\n\n/* breadcrumb transition */\n.breadcrumb-enter-active,\n.breadcrumb-leave-active {\n  transition: all .5s;\n}\n\n.breadcrumb-enter,\n.breadcrumb-leave-active {\n  opacity: 0;\n  transform: translateX(20px);\n}\n\n.breadcrumb-move {\n  transition: all .5s;\n}\n\n.breadcrumb-leave-active {\n  position: absolute;\n}\n"
  },
  {
    "path": "frontend/src/styles/variables.scss",
    "content": "// base color\n$blue:#324157;\n$light-blue:#3A71A8;\n$red:#C03639;\n$pink: #E65D6E;\n$green: #30B08F;\n$tiffany: #4AB7BD;\n$yellow:#FEC171;\n$panGreen: #30B08F;\n\n// sidebar\n$menuText:#bfcbd9;\n$menuActiveText:#409EFF;\n$subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951\n\n$menuBg:#304156;\n$menuHover:#263445;\n\n$subMenuBg:#1f2d3d;\n$subMenuHover:#001528;\n\n$sideBarWidth: 210px;\n\n// the :export directive is the magic sauce for webpack\n// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass\n:export {\n  menuText: $menuText;\n  menuActiveText: $menuActiveText;\n  subMenuActiveText: $subMenuActiveText;\n  menuBg: $menuBg;\n  menuHover: $menuHover;\n  subMenuBg: $subMenuBg;\n  subMenuHover: $subMenuHover;\n  sideBarWidth: $sideBarWidth;\n}\n"
  },
  {
    "path": "frontend/src/utils/auth.js",
    "content": "import Cookies from 'js-cookie'\n\nconst TokenKey = 'Admin-Token'\n\nexport function getToken() {\n  return Cookies.get(TokenKey)\n}\n\nexport function setToken(token) {\n  return Cookies.set(TokenKey, token)\n}\n\nexport function removeToken() {\n  return Cookies.remove(TokenKey)\n}\n"
  },
  {
    "path": "frontend/src/utils/clipboard.js",
    "content": "import Vue from 'vue'\nimport Clipboard from 'clipboard'\n\nfunction clipboardSuccess() {\n  Vue.prototype.$message({\n    message: 'Copy successfully',\n    type: 'success',\n    duration: 1500\n  })\n}\n\nfunction clipboardError() {\n  Vue.prototype.$message({\n    message: 'Copy failed',\n    type: 'error'\n  })\n}\n\nexport default function handleClipboard(text, event) {\n  const clipboard = new Clipboard(event.target, {\n    text: () => text\n  })\n  clipboard.on('success', () => {\n    clipboardSuccess()\n    clipboard.destroy()\n  })\n  clipboard.on('error', () => {\n    clipboardError()\n    clipboard.destroy()\n  })\n  clipboard.onClick(event)\n}\n"
  },
  {
    "path": "frontend/src/utils/get-page-title.js",
    "content": "import defaultSettings from '@/settings'\n\nconst title = defaultSettings.title || 'Vue Element Admin'\n\nexport default function getPageTitle(pageTitle) {\n  if (pageTitle) {\n    return `${pageTitle} | ${title}`\n  }\n  return `${title}`\n}\n"
  },
  {
    "path": "frontend/src/utils/i18n.js",
    "content": "// translate router.meta.title, be used in breadcrumb sidebar tagsview\nexport function generateTitle(title) {\n  const hasKey = this.$te('route.' + title)\n\n  if (hasKey) {\n    // $t :this method from vue-i18n, inject in @/lang/index.js\n    const translatedTitle = this.$t('route.' + title)\n\n    return translatedTitle\n  }\n  return title\n}\n"
  },
  {
    "path": "frontend/src/utils/index.js",
    "content": "/**\n * Created by PanJiaChen on 16/11/18.\n */\n\n/**\n * Parse the time to string\n * @param {(Object|string|number)} time\n * @param {string} cFormat\n * @returns {string | null}\n */\nexport function parseTime(time, cFormat) {\n  if (arguments.length === 0) {\n    return null\n  }\n  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'\n  let date\n  if (typeof time === 'object') {\n    date = time\n  } else {\n    if ((typeof time === 'string')) {\n      if ((/^[0-9]+$/.test(time))) {\n        // support \"1548221490638\"\n        time = parseInt(time)\n      } else {\n        // support safari\n        // https://stackoverflow.com/questions/4310953/invalid-date-in-safari\n        time = time.replace(new RegExp(/-/gm), '/')\n      }\n    }\n\n    if ((typeof time === 'number') && (time.toString().length === 10)) {\n      time = time * 1000\n    }\n    date = new Date(time)\n  }\n  const formatObj = {\n    y: date.getFullYear(),\n    m: date.getMonth() + 1,\n    d: date.getDate(),\n    h: date.getHours(),\n    i: date.getMinutes(),\n    s: date.getSeconds(),\n    a: date.getDay()\n  }\n  const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {\n    const value = formatObj[key]\n    // Note: getDay() returns 0 on Sunday\n    if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }\n    return value.toString().padStart(2, '0')\n  })\n  return time_str\n}\n\n/**\n * @param {number} time\n * @param {string} option\n * @returns {string}\n */\nexport function formatTime(time, option) {\n  if (('' + time).length === 10) {\n    time = parseInt(time) * 1000\n  } else {\n    time = +time\n  }\n  const d = new Date(time)\n  const now = Date.now()\n\n  const diff = (now - d) / 1000\n\n  if (diff < 30) {\n    return '刚刚'\n  } else if (diff < 3600) {\n    // less 1 hour\n    return Math.ceil(diff / 60) + '分钟前'\n  } else if (diff < 3600 * 24) {\n    return Math.ceil(diff / 3600) + '小时前'\n  } else if (diff < 3600 * 24 * 2) {\n    return '1天前'\n  }\n  if (option) {\n    return parseTime(time, option)\n  } else {\n    return (\n      d.getMonth() +\n      1 +\n      '月' +\n      d.getDate() +\n      '日' +\n      d.getHours() +\n      '时' +\n      d.getMinutes() +\n      '分'\n    )\n  }\n}\n\n/**\n * @param {string} url\n * @returns {Object}\n */\nexport function getQueryObject(url) {\n  url = url == null ? window.location.href : url\n  const search = url.substring(url.lastIndexOf('?') + 1)\n  const obj = {}\n  const reg = /([^?&=]+)=([^?&=]*)/g\n  search.replace(reg, (rs, $1, $2) => {\n    const name = decodeURIComponent($1)\n    let val = decodeURIComponent($2)\n    val = String(val)\n    obj[name] = val\n    return rs\n  })\n  return obj\n}\n\n/**\n * @param {string} input value\n * @returns {number} output value\n */\nexport function byteLength(str) {\n  // returns the byte length of an utf8 string\n  let s = str.length\n  for (var i = str.length - 1; i >= 0; i--) {\n    const code = str.charCodeAt(i)\n    if (code > 0x7f && code <= 0x7ff) s++\n    else if (code > 0x7ff && code <= 0xffff) s += 2\n    if (code >= 0xDC00 && code <= 0xDFFF) i--\n  }\n  return s\n}\n\n/**\n * @param {Array} actual\n * @returns {Array}\n */\nexport function cleanArray(actual) {\n  const newArray = []\n  for (let i = 0; i < actual.length; i++) {\n    if (actual[i]) {\n      newArray.push(actual[i])\n    }\n  }\n  return newArray\n}\n\n/**\n * @param {Object} json\n * @returns {Array}\n */\nexport function param(json) {\n  if (!json) return ''\n  return cleanArray(\n    Object.keys(json).map(key => {\n      if (json[key] === undefined) return ''\n      return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])\n    })\n  ).join('&')\n}\n\n/**\n * @param {string} url\n * @returns {Object}\n */\nexport function param2Obj(url) {\n  const search = url.split('?')[1]\n  if (!search) {\n    return {}\n  }\n  return JSON.parse(\n    '{\"' +\n      decodeURIComponent(search)\n        .replace(/\"/g, '\\\\\"')\n        .replace(/&/g, '\",\"')\n        .replace(/=/g, '\":\"')\n        .replace(/\\+/g, ' ') +\n      '\"}'\n  )\n}\n\n/**\n * @param {string} val\n * @returns {string}\n */\nexport function html2Text(val) {\n  const div = document.createElement('div')\n  div.innerHTML = val\n  return div.textContent || div.innerText\n}\n\n/**\n * Merges two objects, giving the last one precedence\n * @param {Object} target\n * @param {(Object|Array)} source\n * @returns {Object}\n */\nexport function objectMerge(target, source) {\n  if (typeof target !== 'object') {\n    target = {}\n  }\n  if (Array.isArray(source)) {\n    return source.slice()\n  }\n  Object.keys(source).forEach(property => {\n    const sourceProperty = source[property]\n    if (typeof sourceProperty === 'object') {\n      target[property] = objectMerge(target[property], sourceProperty)\n    } else {\n      target[property] = sourceProperty\n    }\n  })\n  return target\n}\n\n/**\n * @param {HTMLElement} element\n * @param {string} className\n */\nexport function toggleClass(element, className) {\n  if (!element || !className) {\n    return\n  }\n  let classString = element.className\n  const nameIndex = classString.indexOf(className)\n  if (nameIndex === -1) {\n    classString += '' + className\n  } else {\n    classString =\n      classString.substr(0, nameIndex) +\n      classString.substr(nameIndex + className.length)\n  }\n  element.className = classString\n}\n\n/**\n * @param {string} type\n * @returns {Date}\n */\nexport function getTime(type) {\n  if (type === 'start') {\n    return new Date().getTime() - 3600 * 1000 * 24 * 90\n  } else {\n    return new Date(new Date().toDateString())\n  }\n}\n\n/**\n * @param {Function} func\n * @param {number} wait\n * @param {boolean} immediate\n * @return {*}\n */\nexport function debounce(func, wait, immediate) {\n  let timeout, args, context, timestamp, result\n\n  const later = function() {\n    // 据上一次触发时间间隔\n    const last = +new Date() - timestamp\n\n    // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait\n    if (last < wait && last > 0) {\n      timeout = setTimeout(later, wait - last)\n    } else {\n      timeout = null\n      // 如果设定为immediate===true，因为开始边界已经调用过了此处无需调用\n      if (!immediate) {\n        result = func.apply(context, args)\n        if (!timeout) context = args = null\n      }\n    }\n  }\n\n  return function(...args) {\n    context = this\n    timestamp = +new Date()\n    const callNow = immediate && !timeout\n    // 如果延时不存在，重新设定延时\n    if (!timeout) timeout = setTimeout(later, wait)\n    if (callNow) {\n      result = func.apply(context, args)\n      context = args = null\n    }\n\n    return result\n  }\n}\n\n/**\n * This is just a simple version of deep copy\n * Has a lot of edge cases bug\n * If you want to use a perfect deep copy, use lodash's _.cloneDeep\n * @param {Object} source\n * @returns {Object}\n */\nexport function deepClone(source) {\n  if (!source && typeof source !== 'object') {\n    throw new Error('error arguments', 'deepClone')\n  }\n  const targetObj = source.constructor === Array ? [] : {}\n  Object.keys(source).forEach(keys => {\n    if (source[keys] && typeof source[keys] === 'object') {\n      targetObj[keys] = deepClone(source[keys])\n    } else {\n      targetObj[keys] = source[keys]\n    }\n  })\n  return targetObj\n}\n\n/**\n * @param {Array} arr\n * @returns {Array}\n */\nexport function uniqueArr(arr) {\n  return Array.from(new Set(arr))\n}\n\n/**\n * @returns {string}\n */\nexport function createUniqueString() {\n  const timestamp = +new Date() + ''\n  const randomNum = parseInt((1 + Math.random()) * 65536) + ''\n  return (+(randomNum + timestamp)).toString(32)\n}\n\n/**\n * Check if an element has a class\n * @param {HTMLElement} elm\n * @param {string} cls\n * @returns {boolean}\n */\nexport function hasClass(ele, cls) {\n  return !!ele.className.match(new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)'))\n}\n\n/**\n * Add class to element\n * @param {HTMLElement} elm\n * @param {string} cls\n */\nexport function addClass(ele, cls) {\n  if (!hasClass(ele, cls)) ele.className += ' ' + cls\n}\n\n/**\n * Remove class from element\n * @param {HTMLElement} elm\n * @param {string} cls\n */\nexport function removeClass(ele, cls) {\n  if (hasClass(ele, cls)) {\n    const reg = new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)')\n    ele.className = ele.className.replace(reg, ' ')\n  }\n}\n\nexport function GenDatetime(date) {\n  const Y = date.getFullYear().toString()\n  const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1)\n  const D = date.getDate() + 1 < 10 ? '0' + date.getDate() : date.getDate()\n  const h = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()\n  const m = date.getMinutes() + 1 < 10 ? '0' + date.getMinutes() : date.getMinutes()\n  const s = date.getSeconds() + 1 < 10 ? '0' + date.getSeconds() : date.getSeconds()\n  const ms = date.getMilliseconds().toString().length < 3 ? '0' + date.getMilliseconds() : date.getMilliseconds()\n  return Y + '-' + M + '-' + D + '-' + h + '-' + m + '-' + s + '-' + ms\n}\n\nexport function groupBy(list, key) {\n  const map = new Map();\n  list.forEach((item) => {\n    const collection = map.get(item[key]);\n    if (!collection) {\n      map.set(item[key], [item]);\n    } else {\n      collection.push(item);\n    }\n  });\n  return map;\n}"
  },
  {
    "path": "frontend/src/utils/open-window.js",
    "content": "/**\n *Created by PanJiaChen on 16/11/29.\n * @param {Sting} url\n * @param {Sting} title\n * @param {Number} w\n * @param {Number} h\n */\nexport default function openWindow(url, title, w, h) {\n  // Fixes dual-screen position                            Most browsers       Firefox\n  const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left\n  const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top\n\n  const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width\n  const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height\n\n  const left = ((width / 2) - (w / 2)) + dualScreenLeft\n  const top = ((height / 2) - (h / 2)) + dualScreenTop\n  const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left)\n\n  // Puts focus on the newWindow\n  if (window.focus) {\n    newWindow.focus()\n  }\n}\n\n"
  },
  {
    "path": "frontend/src/utils/permission.js",
    "content": "import store from '@/store'\n\n/**\n * @param {Array} value\n * @returns {Boolean}\n * @example see @/views/permission/directive.vue\n */\nexport default function checkPermission(value) {\n  if (value && value instanceof Array && value.length > 0) {\n    const roles = store.getters && store.getters.roles\n    const permissionRoles = value\n\n    const hasPermission = roles.some(role => {\n      return permissionRoles.includes(role)\n    })\n\n    if (!hasPermission) {\n      return false\n    }\n    return true\n  } else {\n    console.error(`need roles! Like v-permission=\"['admin','editor']\"`)\n    return false\n  }\n}\n\n/**\n * @param {Array} arr ['add','del','view','update']\n * @param {String} value 'add'\n * @returns {Boolean}\n * @example see @/views/permission/directive.vue\n */\nexport function checkAuth(arr, value) {\n  const permissionarr = arr\n  const permissionvalue = value\n  const hasPermission = permissionarr.includes(permissionvalue)\n  if (!hasPermission) {\n    return false\n  }\n  return true\n}\nexport function checkAuthAdd(arr) {\n  return checkAuth(arr, 'add')\n}\nexport function checkAuthDel(arr) {\n  return checkAuth(arr, 'del')\n}\nexport function checkAuthView(arr) {\n  return checkAuth(arr, 'view')\n}\nexport function checkAuthUpdate(arr) {\n  return checkAuth(arr, 'update')\n}\n"
  },
  {
    "path": "frontend/src/utils/request.js",
    "content": "import axios from 'axios'\nimport { MessageBox, Message } from 'element-ui'\nimport store from '@/store'\nimport { getToken } from '@/utils/auth'\n\n// create an axios instance\nconst service = axios.create({\n  baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url\n  withCredentials: true, // 跨域请求时发送 cookies\n  timeout: 500000 // request timeout\n})\n\n// request interceptor\nservice.interceptors.request.use(\n  config => {\n    // do something before request is sent\n\n    if (store.getters.token) {\n      // let each request carry token\n      // ['X-Token'] is a custom headers key\n      // please modify it according to the actual situation\n      config.headers['Authorization'] = 'core ' + getToken()\n    }\n    return config\n  },\n  error => {\n    // do something with request error\n    console.log(error) // for debug\n    return Promise.reject(error)\n  }\n)\n\n// response interceptor\nservice.interceptors.response.use(\n  /**\n   * If you want to get http information such as headers or status\n   * Please return  response => response\n  */\n\n  /**\n   * Determine the request status by custom code\n   * Here is just an example\n   * You can also judge the status by HTTP Status Code\n   */\n  response => {\n    const res = response.data\n\n    // if the custom code is not 20000, it is judged as an error.\n    if (res.code !== 20000) {\n      Message({\n        message: res.message || 'Error',\n        type: 'error',\n        duration: 5 * 1000\n      })\n\n      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;\n      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {\n        // to re-login\n        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {\n          confirmButtonText: 'Re-Login',\n          cancelButtonText: 'Cancel',\n          type: 'warning'\n        }).then(() => {\n          store.dispatch('user/resetToken').then(() => {\n            location.reload()\n          })\n        })\n      }\n      return Promise.reject(new Error(res.message || 'Error'))\n    } else {\n      return res\n    }\n  },\n  error => {\n    console.log('err' + error) // for debug\n    Message({\n      message: error.message,\n      type: 'error',\n      duration: 5 * 1000\n    })\n    return Promise.reject(error)\n  }\n)\n\nexport default service\n"
  },
  {
    "path": "frontend/src/utils/scroll-to.js",
    "content": "Math.easeInOutQuad = function(t, b, c, d) {\n  t /= d / 2\n  if (t < 1) {\n    return c / 2 * t * t + b\n  }\n  t--\n  return -c / 2 * (t * (t - 2) - 1) + b\n}\n\n// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts\nvar requestAnimFrame = (function() {\n  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }\n})()\n\n/**\n * Because it's so fucking difficult to detect the scrolling element, just move them all\n * @param {number} amount\n */\nfunction move(amount) {\n  document.documentElement.scrollTop = amount\n  document.body.parentNode.scrollTop = amount\n  document.body.scrollTop = amount\n}\n\nfunction position() {\n  return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop\n}\n\n/**\n * @param {number} to\n * @param {number} duration\n * @param {Function} callback\n */\nexport function scrollTo(to, duration, callback) {\n  const start = position()\n  const change = to - start\n  const increment = 20\n  let currentTime = 0\n  duration = (typeof (duration) === 'undefined') ? 500 : duration\n  var animateScroll = function() {\n    // increment the time\n    currentTime += increment\n    // find the value with the quadratic in-out easing function\n    var val = Math.easeInOutQuad(currentTime, start, change, duration)\n    // move the document.body\n    move(val)\n    // do the animation unless its over\n    if (currentTime < duration) {\n      requestAnimFrame(animateScroll)\n    } else {\n      if (callback && typeof (callback) === 'function') {\n        // the animation is done so lets callback\n        callback()\n      }\n    }\n  }\n  animateScroll()\n}\n"
  },
  {
    "path": "frontend/src/utils/validate.js",
    "content": "/**\n * Created by PanJiaChen on 16/11/18.\n */\n\n/**\n * @param {string} path\n * @returns {Boolean}\n */\nexport function isExternal(path) {\n  return /^(https?:|mailto:|tel:)/.test(path)\n}\n\n/**\n * @param {string} str\n * @returns {Boolean}\n */\nexport function validUsername(str) {\n  const valid_map = ['admin', 'editor']\n  return valid_map.indexOf(str.trim()) >= 0\n}\n\n/**\n * @param {string} url\n * @returns {Boolean}\n */\nexport function validURL(url) {\n  const reg = /^(https?|ftp):\\/\\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\\.)*[a-zA-Z0-9-]+\\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\\/($|[a-zA-Z0-9.,?'\\\\+&%$#=~_-]+))*$/\n  return reg.test(url)\n}\n\n/**\n * @param {string} str\n * @returns {Boolean}\n */\nexport function validLowerCase(str) {\n  const reg = /^[a-z]+$/\n  return reg.test(str)\n}\n\n/**\n * @param {string} str\n * @returns {Boolean}\n */\nexport function validUpperCase(str) {\n  const reg = /^[A-Z]+$/\n  return reg.test(str)\n}\n\n/**\n * @param {string} str\n * @returns {Boolean}\n */\nexport function validAlphabets(str) {\n  const reg = /^[A-Za-z]+$/\n  return reg.test(str)\n}\n\n/**\n * @param {string} email\n * @returns {Boolean}\n */\nexport function validEmail(email) {\n  const reg = /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/\n  return reg.test(email)\n}\n\n/**\n * @param {string} str\n * @returns {Boolean}\n */\nexport function isString(str) {\n  if (typeof str === 'string' || str instanceof String) {\n    return true\n  }\n  return false\n}\n\n/**\n * @param {Array} arg\n * @returns {Boolean}\n */\nexport function isArray(arg) {\n  if (typeof Array.isArray === 'undefined') {\n    return Object.prototype.toString.call(arg) === '[object Array]'\n  }\n  return Array.isArray(arg)\n}\n"
  },
  {
    "path": "frontend/src/vendor/Export2Excel.js",
    "content": "/* eslint-disable */\nimport { saveAs } from 'file-saver'\nimport XLSX from 'xlsx'\n\nfunction generateArray(table) {\n  var out = [];\n  var rows = table.querySelectorAll('tr');\n  var ranges = [];\n  for (var R = 0; R < rows.length; ++R) {\n    var outRow = [];\n    var row = rows[R];\n    var columns = row.querySelectorAll('td');\n    for (var C = 0; C < columns.length; ++C) {\n      var cell = columns[C];\n      var colspan = cell.getAttribute('colspan');\n      var rowspan = cell.getAttribute('rowspan');\n      var cellValue = cell.innerText;\n      if (cellValue !== \"\" && cellValue == +cellValue) cellValue = +cellValue;\n\n      //Skip ranges\n      ranges.forEach(function (range) {\n        if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {\n          for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);\n        }\n      });\n\n      //Handle Row Span\n      if (rowspan || colspan) {\n        rowspan = rowspan || 1;\n        colspan = colspan || 1;\n        ranges.push({\n          s: {\n            r: R,\n            c: outRow.length\n          },\n          e: {\n            r: R + rowspan - 1,\n            c: outRow.length + colspan - 1\n          }\n        });\n      };\n\n      //Handle Value\n      outRow.push(cellValue !== \"\" ? cellValue : null);\n\n      //Handle Colspan\n      if (colspan)\n        for (var k = 0; k < colspan - 1; ++k) outRow.push(null);\n    }\n    out.push(outRow);\n  }\n  return [out, ranges];\n};\n\nfunction datenum(v, date1904) {\n  if (date1904) v += 1462;\n  var epoch = Date.parse(v);\n  return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);\n}\n\nfunction sheet_from_array_of_arrays(data, opts) {\n  var ws = {};\n  var range = {\n    s: {\n      c: 10000000,\n      r: 10000000\n    },\n    e: {\n      c: 0,\n      r: 0\n    }\n  };\n  for (var R = 0; R != data.length; ++R) {\n    for (var C = 0; C != data[R].length; ++C) {\n      if (range.s.r > R) range.s.r = R;\n      if (range.s.c > C) range.s.c = C;\n      if (range.e.r < R) range.e.r = R;\n      if (range.e.c < C) range.e.c = C;\n      var cell = {\n        v: data[R][C]\n      };\n      if (cell.v == null) continue;\n      var cell_ref = XLSX.utils.encode_cell({\n        c: C,\n        r: R\n      });\n\n      if (typeof cell.v === 'number') cell.t = 'n';\n      else if (typeof cell.v === 'boolean') cell.t = 'b';\n      else if (cell.v instanceof Date) {\n        cell.t = 'n';\n        cell.z = XLSX.SSF._table[14];\n        cell.v = datenum(cell.v);\n      } else cell.t = 's';\n\n      ws[cell_ref] = cell;\n    }\n  }\n  if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);\n  return ws;\n}\n\nfunction Workbook() {\n  if (!(this instanceof Workbook)) return new Workbook();\n  this.SheetNames = [];\n  this.Sheets = {};\n}\n\nfunction s2ab(s) {\n  var buf = new ArrayBuffer(s.length);\n  var view = new Uint8Array(buf);\n  for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;\n  return buf;\n}\n\nexport function export_table_to_excel(id) {\n  var theTable = document.getElementById(id);\n  var oo = generateArray(theTable);\n  var ranges = oo[1];\n\n  /* original data */\n  var data = oo[0];\n  var ws_name = \"SheetJS\";\n\n  var wb = new Workbook(),\n    ws = sheet_from_array_of_arrays(data);\n\n  /* add ranges to worksheet */\n  // ws['!cols'] = ['apple', 'banan'];\n  ws['!merges'] = ranges;\n\n  /* add worksheet to workbook */\n  wb.SheetNames.push(ws_name);\n  wb.Sheets[ws_name] = ws;\n\n  var wbout = XLSX.write(wb, {\n    bookType: 'xlsx',\n    bookSST: false,\n    type: 'binary'\n  });\n\n  saveAs(new Blob([s2ab(wbout)], {\n    type: \"application/octet-stream\"\n  }), \"test.xlsx\")\n}\n\nexport function export_json_to_excel({\n  multiHeader = [],\n  header,\n  data,\n  filename,\n  merges = [],\n  autoWidth = true,\n  bookType = 'xlsx'\n} = {}) {\n  /* original data */\n  filename = filename || 'excel-list'\n  data = [...data]\n  data.unshift(header);\n\n  for (let i = multiHeader.length - 1; i > -1; i--) {\n    data.unshift(multiHeader[i])\n  }\n\n  var ws_name = \"SheetJS\";\n  var wb = new Workbook(),\n    ws = sheet_from_array_of_arrays(data);\n\n  if (merges.length > 0) {\n    if (!ws['!merges']) ws['!merges'] = [];\n    merges.forEach(item => {\n      ws['!merges'].push(XLSX.utils.decode_range(item))\n    })\n  }\n\n  if (autoWidth) {\n    /*设置worksheet每列的最大宽度*/\n    const colWidth = data.map(row => row.map(val => {\n      /*先判断是否为null/undefined*/\n      if (val == null) {\n        return {\n          'wch': 10\n        };\n      }\n      /*再判断是否为中文*/\n      else if (val.toString().charCodeAt(0) > 255) {\n        return {\n          'wch': val.toString().length * 2\n        };\n      } else {\n        return {\n          'wch': val.toString().length\n        };\n      }\n    }))\n    /*以第一行为初始值*/\n    let result = colWidth[0];\n    for (let i = 1; i < colWidth.length; i++) {\n      for (let j = 0; j < colWidth[i].length; j++) {\n        if (result[j]['wch'] < colWidth[i][j]['wch']) {\n          result[j]['wch'] = colWidth[i][j]['wch'];\n        }\n      }\n    }\n    ws['!cols'] = result;\n  }\n\n  /* add worksheet to workbook */\n  wb.SheetNames.push(ws_name);\n  wb.Sheets[ws_name] = ws;\n\n  var wbout = XLSX.write(wb, {\n    bookType: bookType,\n    bookSST: false,\n    type: 'binary'\n  });\n  saveAs(new Blob([s2ab(wbout)], {\n    type: \"application/octet-stream\"\n  }), `${filename}.${bookType}`);\n}\n"
  },
  {
    "path": "frontend/src/vendor/Export2Zip.js",
    "content": "/* eslint-disable */\nimport { saveAs } from 'file-saver'\nimport JSZip from 'jszip'\n\nexport function export_txt_to_zip(th, jsonData, txtName, zipName) {\n  const zip = new JSZip()\n  const txt_name = txtName || 'file'\n  const zip_name = zipName || 'file'\n  const data = jsonData\n  let txtData = `${th}\\r\\n`\n  data.forEach((row) => {\n    let tempStr = ''\n    tempStr = row.toString()\n    txtData += `${tempStr}\\r\\n`\n  })\n  zip.file(`${txt_name}.txt`, txtData)\n  zip.generateAsync({\n    type: \"blob\"\n  }).then((blob) => {\n    saveAs(blob, `${zip_name}.zip`)\n  }, (err) => {\n    alert('导出失败')\n  })\n}\n"
  },
  {
    "path": "frontend/src/views/components-demo/clipboard.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-tabs v-model=\"activeName\">\n      <el-tab-pane label=\"use clipboard  directly\" name=\"directly\">\n        <el-input v-model=\"inputData\" placeholder=\"Please input\" style=\"width:400px;max-width:100%;\" />\n        <el-button type=\"primary\" icon=\"el-icon-document\" @click=\"handleCopy(inputData,$event)\">\n          copy\n        </el-button>\n      </el-tab-pane>\n      <el-tab-pane label=\"use clipboard by v-directive\" name=\"v-directive\">\n        <el-input v-model=\"inputData\" placeholder=\"Please input\" style=\"width:400px;max-width:100%;\" />\n        <el-button v-clipboard:copy=\"inputData\" v-clipboard:success=\"clipboardSuccess\" type=\"primary\" icon=\"el-icon-document\">\n          copy\n        </el-button>\n      </el-tab-pane>\n    </el-tabs>\n  </div>\n</template>\n\n<script>\nimport clip from '@/utils/clipboard' // use clipboard directly\nimport clipboard from '@/directive/clipboard/index.js' // use clipboard by v-directive\n\nexport default {\n  name: 'ClipboardDemo',\n  directives: {\n    clipboard\n  },\n  data() {\n    return {\n      activeName: 'directly',\n      inputData: 'https://github.com/PanJiaChen/vue-element-admin'\n    }\n  },\n  methods: {\n    handleCopy(text, event) {\n      clip(text, event)\n    },\n    clipboardSuccess() {\n      this.$message({\n        message: 'Copy successfully',\n        type: 'success',\n        duration: 1500\n      })\n    }\n  }\n}\n</script>\n\n"
  },
  {
    "path": "frontend/src/views/components-demo/dnd-list.vue",
    "content": "<template>\n  <div class=\"components-container\">\n    <aside>\n      drag-list base on\n      <a\n        href=\"https://github.com/SortableJS/Vue.Draggable\"\n        target=\"_blank\"\n      >Vue.Draggable</a>\n    </aside>\n    <div class=\"editor-container\">\n      <dnd-list :list1=\"list1\" :list2=\"list2\" list1-title=\"List\" list2-title=\"Article pool\" />\n    </div>\n  </div>\n</template>\n\n<script>\nimport DndList from \"@/components/DndList\";\n\nexport default {\n  name: \"DndListDemo\",\n  components: { DndList },\n  data() {\n    return {\n      all_list: [\n        { id: 1, author: \"admin\", title: \"111\", content: \"111\", type: \"CN\" },\n        { id: 2, author: \"admin\", title: \"222\", content: \"222\", type: \"CN\" },\n        { id: 3, author: \"admin\", title: \"333\", content: \"333\", type: \"CN\" },\n        { id: 4, author: \"admin\", title: \"444\", content: \"444\", type: \"CN\" },\n        { id: 5, author: \"admin\", title: \"555\", content: \"555\", type: \"CN\" },\n        { id: 6, author: \"admin\", title: \"666\", content: \"666\", type: \"CN\" }\n      ],\n      list1: [],\n      list2: []\n    };\n  },\n  created() {\n    this.getData();\n  },\n  methods: {\n    getData() {\n      this.list1 = this.all_list.splice(0, 5);\n      this.list2 = this.all_list;\n    }\n  }\n};\n</script>\n\n"
  },
  {
    "path": "frontend/src/views/components-demo/drag-kanban.vue",
    "content": "<template>\n  <div class=\"components-container board\">\n    <Kanban :key=\"1\" :list=\"list1\" :group=\"group\" class=\"kanban todo\" header-text=\"Todo\" />\n    <Kanban :key=\"2\" :list=\"list2\" :group=\"group\" class=\"kanban working\" header-text=\"Working\" />\n    <Kanban :key=\"3\" :list=\"list3\" :group=\"group\" class=\"kanban done\" header-text=\"Done\" />\n  </div>\n</template>\n<script>\nimport Kanban from '@/components/Kanban'\n\nexport default {\n  name: 'DragKanbanDemo',\n  components: {\n    Kanban\n  },\n  data() {\n    return {\n      group: 'mission',\n      list1: [\n        { name: 'Mission', id: 1 },\n        { name: 'Mission', id: 2 },\n        { name: 'Mission', id: 3 },\n        { name: 'Mission', id: 4 }\n      ],\n      list2: [\n        { name: 'Mission', id: 5 },\n        { name: 'Mission', id: 6 },\n        { name: 'Mission', id: 7 }\n      ],\n      list3: [\n        { name: 'Mission', id: 8 },\n        { name: 'Mission', id: 9 },\n        { name: 'Mission', id: 10 }\n      ]\n    }\n  }\n}\n</script>\n<style lang=\"scss\">\n.board {\n  width: 1000px;\n  margin-left: 20px;\n  display: flex;\n  justify-content: space-around;\n  flex-direction: row;\n  align-items: flex-start;\n}\n.kanban {\n  &.todo {\n    .board-column-header {\n      background: #4A9FF9;\n    }\n  }\n  &.working {\n    .board-column-header {\n      background: #f9944a;\n    }\n  }\n  &.done {\n    .board-column-header {\n      background: #2ac06d;\n    }\n  }\n}\n</style>\n\n"
  },
  {
    "path": "frontend/src/views/components-demo/mixin.vue",
    "content": "<template>\n  <div class=\"mixin-components-container\">\n    <el-row>\n      <el-card class=\"box-card\">\n        <div slot=\"header\" class=\"clearfix\">\n          <span>Buttons</span>\n        </div>\n        <div style=\"margin-bottom:50px;\">\n          <el-col :span=\"4\" class=\"text-center\">\n            <router-link class=\"pan-btn blue-btn\" to=\"/documentation/index\">\n              Documentation\n            </router-link>\n          </el-col>\n          <el-col :span=\"4\" class=\"text-center\">\n            <router-link class=\"pan-btn light-blue-btn\" to=\"/icon/index\">\n              Icons\n            </router-link>\n          </el-col>\n          <el-col :span=\"4\" class=\"text-center\">\n            <router-link class=\"pan-btn pink-btn\" to=\"/excel/export-excel\">\n              Excel\n            </router-link>\n          </el-col>\n          <el-col :span=\"4\" class=\"text-center\">\n            <router-link class=\"pan-btn green-btn\" to=\"/table/complex-table\">\n              Table\n            </router-link>\n          </el-col>\n          <el-col :span=\"4\" class=\"text-center\">\n            <router-link class=\"pan-btn tiffany-btn\" to=\"/example/create\">\n              Form\n            </router-link>\n          </el-col>\n          <el-col :span=\"4\" class=\"text-center\">\n            <router-link class=\"pan-btn yellow-btn\" to=\"/theme/index\">\n              Theme\n            </router-link>\n          </el-col>\n        </div>\n      </el-card>\n    </el-row>\n\n    <el-row :gutter=\"20\" style=\"margin-top:50px;\">\n      <el-col :span=\"6\">\n        <el-card class=\"box-card\">\n          <div slot=\"header\" class=\"clearfix\">\n            <span>Material Design 的input</span>\n          </div>\n          <div style=\"height:100px;\">\n            <el-form :model=\"demo\" :rules=\"demoRules\">\n              <el-form-item prop=\"title\">\n                <md-input v-model=\"demo.title\" icon=\"el-icon-search\" name=\"title\" placeholder=\"输入标题\">\n                  标题\n                </md-input>\n              </el-form-item>\n            </el-form>\n          </div>\n        </el-card>\n      </el-col>\n\n      <el-col :span=\"6\">\n        <el-card class=\"box-card\">\n          <div slot=\"header\" class=\"clearfix\">\n            <span>图片hover效果</span>\n          </div>\n          <div class=\"component-item\">\n            <pan-thumb width=\"100px\" height=\"100px\" image=\"https://wpimg.wallstcn.com/577965b9-bb9e-4e02-9f0c-095b41417191\">\n              vue-element-admin\n            </pan-thumb>\n          </div>\n        </el-card>\n      </el-col>\n\n      <el-col :span=\"6\">\n        <el-card class=\"box-card\">\n          <div slot=\"header\" class=\"clearfix\">\n            <span>水波纹 waves v-directive</span>\n          </div>\n          <div class=\"component-item\">\n            <el-button v-waves type=\"primary\">\n              水波纹效果\n            </el-button>\n          </div>\n        </el-card>\n      </el-col>\n\n      <el-col :span=\"6\">\n        <el-card class=\"box-card\">\n          <div slot=\"header\" class=\"clearfix\">\n            <span>hover text</span>\n          </div>\n          <div class=\"component-item\">\n            <mallki class-name=\"mallki-text\" text=\"vue-element-admin\" />\n          </div>\n        </el-card>\n      </el-col>\n    </el-row>\n\n    <el-row :gutter=\"20\" style=\"margin-top:50px;\">\n      <el-col :span=\"8\">\n        <el-card class=\"box-card\">\n          <div slot=\"header\" class=\"clearfix\">\n            <span>Share</span>\n          </div>\n          <div class=\"component-item\" style=\"height:420px;\">\n            <dropdown-menu :items=\"articleList\" style=\"margin:0 auto;\" title=\"系列文章\" />\n          </div>\n        </el-card>\n      </el-col>\n    </el-row>\n  </div>\n</template>\n\n<script>\nimport PanThumb from '@/components/PanThumb'\nimport MdInput from '@/components/MDinput'\nimport Mallki from '@/components/TextHoverEffect/Mallki'\nimport DropdownMenu from '@/components/Share/DropdownMenu'\nimport waves from '@/directive/waves/index.js' // 水波纹指令\n\nexport default {\n  name: 'ComponentMixinDemo',\n  components: {\n    PanThumb,\n    MdInput,\n    Mallki,\n    DropdownMenu\n  },\n  directives: {\n    waves\n  },\n  data() {\n    const validate = (rule, value, callback) => {\n      if (value.length !== 6) {\n        callback(new Error('请输入六个字符'))\n      } else {\n        callback()\n      }\n    }\n    return {\n      demo: {\n        title: ''\n      },\n      demoRules: {\n        title: [{ required: true, trigger: 'change', validator: validate }]\n      },\n      articleList: [\n        { title: '基础篇', href: 'https://juejin.im/post/59097cd7a22b9d0065fb61d2' },\n        { title: '登录权限篇', href: 'https://juejin.im/post/591aa14f570c35006961acac' },\n        { title: '实战篇', href: 'https://juejin.im/post/593121aa0ce4630057f70d35' },\n        { title: 'vue-admin-template 篇', href: 'https://juejin.im/post/595b4d776fb9a06bbe7dba56' },\n        { title: 'v4.0 篇', href: 'https://juejin.im/post/5c92ff94f265da6128275a85' },\n        { title: '优雅的使用 icon', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' }\n      ]\n    }\n  }\n}\n</script>\n\n<style scoped>\n.mixin-components-container {\n  background-color: #f0f2f5;\n  padding: 30px;\n  min-height: calc(100vh - 84px);\n}\n.component-item{\n  min-height: 100px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/components-demo/sticky.vue",
    "content": "<template>\n  <div>\n    <sticky :z-index=\"10\" class-name=\"sub-navbar\">\n      <el-dropdown trigger=\"click\">\n        <el-button plain>\n          Platform<i class=\"el-icon-caret-bottom el-icon--right\" />\n        </el-button>\n        <el-dropdown-menu slot=\"dropdown\" class=\"no-border\">\n          <el-checkbox-group v-model=\"platforms\" style=\"padding: 5px 15px;\">\n            <el-checkbox v-for=\"item in platformsOptions\" :key=\"item.key\" :label=\"item.key\">\n              {{ item.name }}\n            </el-checkbox>\n          </el-checkbox-group>\n        </el-dropdown-menu>\n      </el-dropdown>\n\n      <el-dropdown trigger=\"click\">\n        <el-button plain>\n          Link<i class=\"el-icon-caret-bottom el-icon--right\" />\n        </el-button>\n        <el-dropdown-menu slot=\"dropdown\" class=\"no-padding no-border\" style=\"width:300px\">\n          <el-input v-model=\"url\" placeholder=\"Please enter the content\">\n            <template slot=\"prepend\">\n              Url\n            </template>\n          </el-input>\n        </el-dropdown-menu>\n      </el-dropdown>\n\n      <div class=\"time-container\">\n        <el-date-picker v-model=\"time\" type=\"datetime\" format=\"yyyy-MM-dd HH:mm:ss\" placeholder=\"Release time\" />\n      </div>\n\n      <el-button style=\"margin-left: 10px;\" type=\"success\">\n        publish\n      </el-button>\n    </sticky>\n\n    <div class=\"components-container\">\n      <aside>\n        Sticky header, When the page is scrolled to the preset position will be sticky on the top.\n      </aside>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <sticky :sticky-top=\"200\">\n        <el-button type=\"primary\"> placeholder</el-button>\n      </sticky>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n      <div>placeholder</div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport Sticky from '@/components/Sticky'\n\nexport default {\n  name: 'StickyDemo',\n  components: { Sticky },\n  data() {\n    return {\n      time: '',\n      url: '',\n      platforms: ['a-platform'],\n      platformsOptions: [\n        { key: 'a-platform', name: 'platformA' },\n        { key: 'b-platform', name: 'platformB' },\n        { key: 'c-platform', name: 'platformC' }\n      ],\n      pickerOptions: {\n        disabledDate(time) {\n          return time.getTime() > Date.now()\n        }\n      }\n    }\n  }\n}\n</script>\n\n<style scoped>\n.components-container div {\n  margin: 10px;\n}\n\n.time-container {\n  display: inline-block;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/dashboard/components/BarChart.vue",
    "content": "<template>\n  <div :class=\"className\" :style=\"{height:height,width:width}\" />\n</template>\n\n<script>\nimport echarts from 'echarts'\nrequire('echarts/theme/macarons') // echarts theme\nimport resize from './mixins/resize'\n\nconst animationDuration = 6000\n\nexport default {\n  mixins: [resize],\n  props: {\n    className: {\n      type: String,\n      default: 'chart'\n    },\n    width: {\n      type: String,\n      default: '100%'\n    },\n    height: {\n      type: String,\n      default: '300px'\n    }\n  },\n  data() {\n    return {\n      chart: null\n    }\n  },\n  mounted() {\n    this.$nextTick(() => {\n      this.initChart()\n    })\n  },\n  beforeDestroy() {\n    if (!this.chart) {\n      return\n    }\n    this.chart.dispose()\n    this.chart = null\n  },\n  methods: {\n    initChart() {\n      this.chart = echarts.init(this.$el, 'macarons')\n\n      this.chart.setOption({\n        tooltip: {\n          trigger: 'axis',\n          axisPointer: { // 坐标轴指示器，坐标轴触发有效\n            type: 'shadow' // 默认为直线，可选为：'line' | 'shadow'\n          }\n        },\n        grid: {\n          top: 10,\n          left: '2%',\n          right: '2%',\n          bottom: '3%',\n          containLabel: true\n        },\n        xAxis: [{\n          type: 'category',\n          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],\n          axisTick: {\n            alignWithLabel: true\n          }\n        }],\n        yAxis: [{\n          type: 'value',\n          axisTick: {\n            show: false\n          }\n        }],\n        series: [{\n          name: 'pageA',\n          type: 'bar',\n          stack: 'vistors',\n          barWidth: '60%',\n          data: [79, 52, 200, 334, 390, 330, 220],\n          animationDuration\n        }, {\n          name: 'pageB',\n          type: 'bar',\n          stack: 'vistors',\n          barWidth: '60%',\n          data: [80, 52, 200, 334, 390, 330, 220],\n          animationDuration\n        }, {\n          name: 'pageC',\n          type: 'bar',\n          stack: 'vistors',\n          barWidth: '60%',\n          data: [30, 52, 200, 334, 390, 330, 220],\n          animationDuration\n        }]\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/views/dashboard/components/BoxCard.vue",
    "content": "<template>\n  <el-card class=\"box-card-component\" style=\"margin-left:8px;\">\n    <div slot=\"header\" class=\"box-card-header\">\n      <img src=\"https://wpimg.wallstcn.com/e7d23d71-cf19-4b90-a1cc-f56af8c0903d.png\">\n    </div>\n    <div style=\"position:relative;\">\n      <pan-thumb :image=\"avatar\" class=\"panThumb\" />\n      <mallki class-name=\"mallki-text\" text=\"vue-element-admin\" />\n      <div style=\"padding-top:35px;\" class=\"progress-item\">\n        <span>Vue</span>\n        <el-progress :percentage=\"70\" />\n      </div>\n      <div class=\"progress-item\">\n        <span>JavaScript</span>\n        <el-progress :percentage=\"18\" />\n      </div>\n      <div class=\"progress-item\">\n        <span>Css</span>\n        <el-progress :percentage=\"12\" />\n      </div>\n      <div class=\"progress-item\">\n        <span>ESLint</span>\n        <el-progress :percentage=\"100\" status=\"success\" />\n      </div>\n    </div>\n  </el-card>\n</template>\n\n<script>\nimport { mapGetters } from 'vuex'\nimport PanThumb from '@/components/PanThumb'\nimport Mallki from '@/components/TextHoverEffect/Mallki'\n\nexport default {\n  components: { PanThumb, Mallki },\n\n  filters: {\n    statusFilter(status) {\n      const statusMap = {\n        success: 'success',\n        pending: 'danger'\n      }\n      return statusMap[status]\n    }\n  },\n  data() {\n    return {\n      statisticsData: {\n        article_count: 1024,\n        pageviews_count: 1024\n      }\n    }\n  },\n  computed: {\n    ...mapGetters([\n      'name',\n      'avatar',\n      'roles'\n    ])\n  }\n}\n</script>\n\n<style lang=\"scss\" >\n.box-card-component{\n  .el-card__header {\n    padding: 0px!important;\n  }\n}\n</style>\n<style lang=\"scss\" scoped>\n.box-card-component {\n  .box-card-header {\n    position: relative;\n    height: 220px;\n    img {\n      width: 100%;\n      height: 100%;\n      transition: all 0.2s linear;\n      &:hover {\n        transform: scale(1.1, 1.1);\n        filter: contrast(130%);\n      }\n    }\n  }\n  .mallki-text {\n    position: absolute;\n    top: 0px;\n    right: 0px;\n    font-size: 20px;\n    font-weight: bold;\n  }\n  .panThumb {\n    z-index: 100;\n    height: 70px!important;\n    width: 70px!important;\n    position: absolute!important;\n    top: -45px;\n    left: 0px;\n    border: 5px solid #ffffff;\n    background-color: #fff;\n    margin: auto;\n    box-shadow: none!important;\n    /deep/ .pan-info {\n      box-shadow: none!important;\n    }\n  }\n  .progress-item {\n    margin-bottom: 10px;\n    font-size: 14px;\n  }\n  @media only screen and (max-width: 1510px){\n    .mallki-text{\n      display: none;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/dashboard/components/LineChart.vue",
    "content": "<template>\n  <div :class=\"className\" :style=\"{height:height,width:width}\" />\n</template>\n\n<script>\nimport echarts from 'echarts'\nrequire('echarts/theme/macarons') // echarts theme\nimport resize from './mixins/resize'\n\nexport default {\n  mixins: [resize],\n  props: {\n    className: {\n      type: String,\n      default: 'chart'\n    },\n    width: {\n      type: String,\n      default: '100%'\n    },\n    height: {\n      type: String,\n      default: '350px'\n    },\n    autoResize: {\n      type: Boolean,\n      default: true\n    },\n    chartData: {\n      type: Object,\n      required: true\n    }\n  },\n  data() {\n    return {\n      chart: null\n    }\n  },\n  watch: {\n    chartData: {\n      deep: true,\n      handler(val) {\n        this.setOptions(val)\n      }\n    }\n  },\n  mounted() {\n    this.$nextTick(() => {\n      this.initChart()\n    })\n  },\n  beforeDestroy() {\n    if (!this.chart) {\n      return\n    }\n    this.chart.dispose()\n    this.chart = null\n  },\n  methods: {\n    initChart() {\n      this.chart = echarts.init(this.$el, 'macarons')\n      this.setOptions(this.chartData)\n    },\n    setOptions({ expectedData, actualData } = {}) {\n      this.chart.setOption({\n        xAxis: {\n          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],\n          boundaryGap: false,\n          axisTick: {\n            show: false\n          }\n        },\n        grid: {\n          left: 10,\n          right: 10,\n          bottom: 20,\n          top: 30,\n          containLabel: true\n        },\n        tooltip: {\n          trigger: 'axis',\n          axisPointer: {\n            type: 'cross'\n          },\n          padding: [5, 10]\n        },\n        yAxis: {\n          axisTick: {\n            show: false\n          }\n        },\n        legend: {\n          data: ['expected', 'actual']\n        },\n        series: [{\n          name: 'expected', itemStyle: {\n            normal: {\n              color: '#FF005A',\n              lineStyle: {\n                color: '#FF005A',\n                width: 2\n              }\n            }\n          },\n          smooth: true,\n          type: 'line',\n          data: expectedData,\n          animationDuration: 2800,\n          animationEasing: 'cubicInOut'\n        },\n        {\n          name: 'actual',\n          smooth: true,\n          type: 'line',\n          itemStyle: {\n            normal: {\n              color: '#3888fa',\n              lineStyle: {\n                color: '#3888fa',\n                width: 2\n              },\n              areaStyle: {\n                color: '#f3f8ff'\n              }\n            }\n          },\n          data: actualData,\n          animationDuration: 2800,\n          animationEasing: 'quadraticOut'\n        }]\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/views/dashboard/components/PanelGroup.vue",
    "content": "<template>\n  <el-row :gutter=\"40\" class=\"panel-group\">\n    <el-col :xs=\"12\" :sm=\"12\" :lg=\"6\" class=\"card-panel-col\">\n      <div class=\"card-panel\" @click=\"handleSetLineChartData('newVisitis')\">\n        <div class=\"card-panel-icon-wrapper icon-people\">\n          <svg-icon icon-class=\"group\" class-name=\"card-panel-icon\" />\n        </div>\n        <div class=\"card-panel-description\">\n          <div class=\"card-panel-text\">\n            New Visits\n          </div>\n          <count-to :start-val=\"0\" :end-val=\"102400\" :duration=\"2600\" class=\"card-panel-num\" />\n        </div>\n      </div>\n    </el-col>\n    <el-col :xs=\"12\" :sm=\"12\" :lg=\"6\" class=\"card-panel-col\">\n      <div class=\"card-panel\" @click=\"handleSetLineChartData('messages')\">\n        <div class=\"card-panel-icon-wrapper icon-message\">\n          <svg-icon icon-class=\"message\" class-name=\"card-panel-icon\" />\n        </div>\n        <div class=\"card-panel-description\">\n          <div class=\"card-panel-text\">\n            Messages\n          </div>\n          <count-to :start-val=\"0\" :end-val=\"81212\" :duration=\"3000\" class=\"card-panel-num\" />\n        </div>\n      </div>\n    </el-col>\n    <el-col :xs=\"12\" :sm=\"12\" :lg=\"6\" class=\"card-panel-col\">\n      <div class=\"card-panel\" @click=\"handleSetLineChartData('purchases')\">\n        <div class=\"card-panel-icon-wrapper icon-money\">\n          <svg-icon icon-class=\"money\" class-name=\"card-panel-icon\" />\n        </div>\n        <div class=\"card-panel-description\">\n          <div class=\"card-panel-text\">\n            Purchases\n          </div>\n          <count-to :start-val=\"0\" :end-val=\"9280\" :duration=\"3200\" class=\"card-panel-num\" />\n        </div>\n      </div>\n    </el-col>\n    <el-col :xs=\"12\" :sm=\"12\" :lg=\"6\" class=\"card-panel-col\">\n      <div class=\"card-panel\" @click=\"handleSetLineChartData('shoppings')\">\n        <div class=\"card-panel-icon-wrapper icon-shopping\">\n          <svg-icon icon-class=\"shopping\" class-name=\"card-panel-icon\" />\n        </div>\n        <div class=\"card-panel-description\">\n          <div class=\"card-panel-text\">\n            Shoppings\n          </div>\n          <count-to :start-val=\"0\" :end-val=\"13600\" :duration=\"3600\" class=\"card-panel-num\" />\n        </div>\n      </div>\n    </el-col>\n  </el-row>\n</template>\n\n<script>\nimport CountTo from 'vue-count-to'\n\nexport default {\n  components: {\n    CountTo\n  },\n  methods: {\n    handleSetLineChartData(type) {\n      this.$emit('handleSetLineChartData', type)\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.panel-group {\n  margin-top: 18px;\n\n  .card-panel-col {\n    margin-bottom: 32px;\n  }\n\n  .card-panel {\n    height: 108px;\n    cursor: pointer;\n    font-size: 12px;\n    position: relative;\n    overflow: hidden;\n    color: #666;\n    background: #fff;\n    box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);\n    border-color: rgba(0, 0, 0, .05);\n\n    &:hover {\n      .card-panel-icon-wrapper {\n        color: #fff;\n      }\n\n      .icon-people {\n        background: #40c9c6;\n      }\n\n      .icon-message {\n        background: #36a3f7;\n      }\n\n      .icon-money {\n        background: #f4516c;\n      }\n\n      .icon-shopping {\n        background: #34bfa3\n      }\n    }\n\n    .icon-people {\n      color: #40c9c6;\n    }\n\n    .icon-message {\n      color: #36a3f7;\n    }\n\n    .icon-money {\n      color: #f4516c;\n    }\n\n    .icon-shopping {\n      color: #34bfa3\n    }\n\n    .card-panel-icon-wrapper {\n      float: left;\n      margin: 14px 0 0 14px;\n      padding: 16px;\n      transition: all 0.38s ease-out;\n      border-radius: 6px;\n    }\n\n    .card-panel-icon {\n      float: left;\n      font-size: 48px;\n    }\n\n    .card-panel-description {\n      float: right;\n      font-weight: bold;\n      margin: 26px;\n      margin-left: 0px;\n\n      .card-panel-text {\n        line-height: 18px;\n        color: rgba(0, 0, 0, 0.45);\n        font-size: 16px;\n        margin-bottom: 12px;\n      }\n\n      .card-panel-num {\n        font-size: 20px;\n      }\n    }\n  }\n}\n\n@media (max-width:550px) {\n  .card-panel-description {\n    display: none;\n  }\n\n  .card-panel-icon-wrapper {\n    float: none !important;\n    width: 100%;\n    height: 100%;\n    margin: 0 !important;\n\n    .svg-icon {\n      display: block;\n      margin: 14px auto !important;\n      float: none !important;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/dashboard/components/PieChart.vue",
    "content": "<template>\n  <div :class=\"className\" :style=\"{height:height,width:width}\" />\n</template>\n\n<script>\nimport echarts from 'echarts'\nrequire('echarts/theme/macarons') // echarts theme\nimport resize from './mixins/resize'\n\nexport default {\n  mixins: [resize],\n  props: {\n    className: {\n      type: String,\n      default: 'chart'\n    },\n    width: {\n      type: String,\n      default: '100%'\n    },\n    height: {\n      type: String,\n      default: '300px'\n    }\n  },\n  data() {\n    return {\n      chart: null\n    }\n  },\n  mounted() {\n    this.$nextTick(() => {\n      this.initChart()\n    })\n  },\n  beforeDestroy() {\n    if (!this.chart) {\n      return\n    }\n    this.chart.dispose()\n    this.chart = null\n  },\n  methods: {\n    initChart() {\n      this.chart = echarts.init(this.$el, 'macarons')\n\n      this.chart.setOption({\n        tooltip: {\n          trigger: 'item',\n          formatter: '{a} <br/>{b} : {c} ({d}%)'\n        },\n        legend: {\n          left: 'center',\n          bottom: '10',\n          data: ['Industries', 'Technology', 'Forex', 'Gold', 'Forecasts']\n        },\n        series: [\n          {\n            name: 'WEEKLY WRITE ARTICLES',\n            type: 'pie',\n            roseType: 'radius',\n            radius: [15, 95],\n            center: ['50%', '38%'],\n            data: [\n              { value: 320, name: 'Industries' },\n              { value: 240, name: 'Technology' },\n              { value: 149, name: 'Forex' },\n              { value: 100, name: 'Gold' },\n              { value: 59, name: 'Forecasts' }\n            ],\n            animationEasing: 'cubicInOut',\n            animationDuration: 2600\n          }\n        ]\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/views/dashboard/components/RaddarChart.vue",
    "content": "<template>\n  <div :class=\"className\" :style=\"{height:height,width:width}\" />\n</template>\n\n<script>\nimport echarts from 'echarts'\nrequire('echarts/theme/macarons') // echarts theme\nimport resize from './mixins/resize'\n\nconst animationDuration = 3000\n\nexport default {\n  mixins: [resize],\n  props: {\n    className: {\n      type: String,\n      default: 'chart'\n    },\n    width: {\n      type: String,\n      default: '100%'\n    },\n    height: {\n      type: String,\n      default: '300px'\n    }\n  },\n  data() {\n    return {\n      chart: null\n    }\n  },\n  mounted() {\n    this.$nextTick(() => {\n      this.initChart()\n    })\n  },\n  beforeDestroy() {\n    if (!this.chart) {\n      return\n    }\n    this.chart.dispose()\n    this.chart = null\n  },\n  methods: {\n    initChart() {\n      this.chart = echarts.init(this.$el, 'macarons')\n\n      this.chart.setOption({\n        tooltip: {\n          trigger: 'axis',\n          axisPointer: { // 坐标轴指示器，坐标轴触发有效\n            type: 'shadow' // 默认为直线，可选为：'line' | 'shadow'\n          }\n        },\n        radar: {\n          radius: '66%',\n          center: ['50%', '42%'],\n          splitNumber: 8,\n          splitArea: {\n            areaStyle: {\n              color: 'rgba(127,95,132,.3)',\n              opacity: 1,\n              shadowBlur: 45,\n              shadowColor: 'rgba(0,0,0,.5)',\n              shadowOffsetX: 0,\n              shadowOffsetY: 15\n            }\n          },\n          indicator: [\n            { name: 'Sales', max: 10000 },\n            { name: 'Administration', max: 20000 },\n            { name: 'Information Technology', max: 20000 },\n            { name: 'Customer Support', max: 20000 },\n            { name: 'Development', max: 20000 },\n            { name: 'Marketing', max: 20000 }\n          ]\n        },\n        legend: {\n          left: 'center',\n          bottom: '10',\n          data: ['Allocated Budget', 'Expected Spending', 'Actual Spending']\n        },\n        series: [{\n          type: 'radar',\n          symbolSize: 0,\n          areaStyle: {\n            normal: {\n              shadowBlur: 13,\n              shadowColor: 'rgba(0,0,0,.2)',\n              shadowOffsetX: 0,\n              shadowOffsetY: 10,\n              opacity: 1\n            }\n          },\n          data: [\n            {\n              value: [5000, 7000, 12000, 11000, 15000, 14000],\n              name: 'Allocated Budget'\n            },\n            {\n              value: [4000, 9000, 15000, 15000, 13000, 11000],\n              name: 'Expected Spending'\n            },\n            {\n              value: [5500, 11000, 12000, 15000, 12000, 12000],\n              name: 'Actual Spending'\n            }\n          ],\n          animationDuration: animationDuration\n        }]\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/views/dashboard/components/TodoList/Todo.vue",
    "content": "<template>\n  <li :class=\"{ completed: todo.done, editing: editing }\" class=\"todo\">\n    <div class=\"view\">\n      <input\n        :checked=\"todo.done\"\n        class=\"toggle\"\n        type=\"checkbox\"\n        @change=\"toggleTodo( todo)\"\n      >\n      <label @dblclick=\"editing = true\" v-text=\"todo.text\" />\n      <button class=\"destroy\" @click=\"deleteTodo( todo )\" />\n    </div>\n    <input\n      v-show=\"editing\"\n      v-focus=\"editing\"\n      :value=\"todo.text\"\n      class=\"edit\"\n      @keyup.enter=\"doneEdit\"\n      @keyup.esc=\"cancelEdit\"\n      @blur=\"doneEdit\"\n    >\n  </li>\n</template>\n\n<script>\nexport default {\n  name: 'Todo',\n  directives: {\n    focus(el, { value }, { context }) {\n      if (value) {\n        context.$nextTick(() => {\n          el.focus()\n        })\n      }\n    }\n  },\n  props: {\n    todo: {\n      type: Object,\n      default: function() {\n        return {}\n      }\n    }\n  },\n  data() {\n    return {\n      editing: false\n    }\n  },\n  methods: {\n    deleteTodo(todo) {\n      this.$emit('deleteTodo', todo)\n    },\n    editTodo({ todo, value }) {\n      this.$emit('editTodo', { todo, value })\n    },\n    toggleTodo(todo) {\n      this.$emit('toggleTodo', todo)\n    },\n    doneEdit(e) {\n      const value = e.target.value.trim()\n      const { todo } = this\n      if (!value) {\n        this.deleteTodo({\n          todo\n        })\n      } else if (this.editing) {\n        this.editTodo({\n          todo,\n          value\n        })\n        this.editing = false\n      }\n    },\n    cancelEdit(e) {\n      e.target.value = this.todo.text\n      this.editing = false\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "frontend/src/views/dashboard/components/TodoList/index.scss",
    "content": ".todoapp {\n  font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;\n  line-height: 1.4em;\n  color: #4d4d4d;\n  min-width: 230px;\n  max-width: 550px;\n  margin: 0 auto ;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  font-weight: 300;\n  background: #fff;\n  z-index: 1;\n  position: relative;\n  button {\n    margin: 0;\n    padding: 0;\n    border: 0;\n    background: none;\n    font-size: 100%;\n    vertical-align: baseline;\n    font-family: inherit;\n    font-weight: inherit;\n    color: inherit;\n    -webkit-appearance: none;\n    appearance: none;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n  }\n  :focus {\n    outline: 0;\n  }\n  .hidden {\n    display: none;\n  }\n  .todoapp {\n    background: #fff;\n    margin: 130px 0 40px 0;\n    position: relative;\n    box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);\n  }\n  .todoapp input::-webkit-input-placeholder {\n    font-style: italic;\n    font-weight: 300;\n    color: #e6e6e6;\n  }\n  .todoapp input::-moz-placeholder {\n    font-style: italic;\n    font-weight: 300;\n    color: #e6e6e6;\n  }\n  .todoapp input::input-placeholder {\n    font-style: italic;\n    font-weight: 300;\n    color: #e6e6e6;\n  }\n  .todoapp h1 {\n    position: absolute;\n    top: -155px;\n    width: 100%;\n    font-size: 100px;\n    font-weight: 100;\n    text-align: center;\n    color: rgba(175, 47, 47, 0.15);\n    -webkit-text-rendering: optimizeLegibility;\n    -moz-text-rendering: optimizeLegibility;\n    text-rendering: optimizeLegibility;\n  }\n  .new-todo,\n  .edit {\n    position: relative;\n    margin: 0;\n    width: 100%;\n    font-size: 18px;\n    font-family: inherit;\n    font-weight: inherit;\n    line-height: 1.4em;\n    border: 0;\n    color: inherit;\n    padding: 6px;\n    border: 1px solid #999;\n    box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);\n    box-sizing: border-box;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n  }\n  .new-todo {\n    padding: 10px 16px 16px 60px;\n    border: none;\n    background: rgba(0, 0, 0, 0.003);\n    box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);\n  }\n  .main {\n    position: relative;\n    z-index: 2;\n    border-top: 1px solid #e6e6e6;\n  }\n  .toggle-all {\n    text-align: center;\n    border: none;\n    /* Mobile Safari */\n    opacity: 0;\n    position: absolute;\n  }\n  .toggle-all+label {\n    width: 60px;\n    height: 34px;\n    font-size: 0;\n    position: absolute;\n    top: -52px;\n    left: -13px;\n    -webkit-transform: rotate(90deg);\n    transform: rotate(90deg);\n  }\n  .toggle-all+label:before {\n    content: '❯';\n    font-size: 22px;\n    color: #e6e6e6;\n    padding: 10px 27px 10px 27px;\n  }\n  .toggle-all:checked+label:before {\n    color: #737373;\n  }\n  .todo-list {\n    margin: 0;\n    padding: 0;\n    list-style: none;\n  }\n  .todo-list li {\n    position: relative;\n    font-size: 24px;\n    border-bottom: 1px solid #ededed;\n  }\n  .todo-list li:last-child {\n    border-bottom: none;\n  }\n  .todo-list li.editing {\n    border-bottom: none;\n    padding: 0;\n  }\n  .todo-list li.editing .edit {\n    display: block;\n    width: 506px;\n    padding: 12px 16px;\n    margin: 0 0 0 43px;\n  }\n  .todo-list li.editing .view {\n    display: none;\n  }\n  .todo-list li .toggle {\n    text-align: center;\n    width: 40px;\n    /* auto, since non-WebKit browsers doesn't support input styling */\n    height: auto;\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    margin: auto 0;\n    border: none;\n    /* Mobile Safari */\n    -webkit-appearance: none;\n    appearance: none;\n  }\n  .todo-list li .toggle {\n    opacity: 0;\n  }\n  .todo-list li .toggle+label {\n    /*\n    Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433\n    IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/\n  */\n    background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');\n    background-repeat: no-repeat;\n    background-position: center left;\n    background-size: 36px;\n  }\n  .todo-list li .toggle:checked+label {\n    background-size: 36px;\n    background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');\n  }\n  .todo-list li label {\n    word-break: break-all;\n    padding: 15px 15px 15px 50px;\n    display: block;\n    line-height: 1.0;\n        font-size: 14px;\n    transition: color 0.4s;\n  }\n  .todo-list li.completed label {\n    color: #d9d9d9;\n    text-decoration: line-through;\n  }\n  .todo-list li .destroy {\n    display: none;\n    position: absolute;\n    top: 0;\n    right: 10px;\n    bottom: 0;\n    width: 40px;\n    height: 40px;\n    margin: auto 0;\n    font-size: 30px;\n    color: #cc9a9a;\n    transition: color 0.2s ease-out;\n    cursor: pointer;\n  }\n  .todo-list li .destroy:hover {\n    color: #af5b5e;\n  }\n  .todo-list li .destroy:after {\n    content: '×';\n  }\n  .todo-list li:hover .destroy {\n    display: block;\n  }\n  .todo-list li .edit {\n    display: none;\n  }\n  .todo-list li.editing:last-child {\n    margin-bottom: -1px;\n  }\n  .footer {\n    color: #777;\n    position: relative;\n    padding: 10px 15px;\n    height: 40px;\n    text-align: center;\n    border-top: 1px solid #e6e6e6;\n  }\n  .footer:before {\n    content: '';\n    position: absolute;\n    right: 0;\n    bottom: 0;\n    left: 0;\n    height: 40px;\n    overflow: hidden;\n    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6, 0 17px 2px -6px rgba(0, 0, 0, 0.2);\n  }\n  .todo-count {\n    float: left;\n    text-align: left;\n  }\n  .todo-count strong {\n    font-weight: 300;\n  }\n  .filters {\n    margin: 0;\n    padding: 0;\n    position: relative;\n    z-index: 1;\n    list-style: none;\n  }\n  .filters li {\n    display: inline;\n  }\n  .filters li a {\n    color: inherit;\n    font-size: 12px;\n    padding: 3px 7px;\n    text-decoration: none;\n    border: 1px solid transparent;\n    border-radius: 3px;\n  }\n  .filters li a:hover {\n    border-color: rgba(175, 47, 47, 0.1);\n  }\n  .filters li a.selected {\n    border-color: rgba(175, 47, 47, 0.2);\n  }\n  .clear-completed,\n  html .clear-completed:active {\n    float: right;\n    position: relative;\n    line-height: 20px;\n    text-decoration: none;\n    cursor: pointer;\n  }\n  .clear-completed:hover {\n    text-decoration: underline;\n  }\n  .info {\n    margin: 65px auto 0;\n    color: #bfbfbf;\n    font-size: 10px;\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);\n    text-align: center;\n  }\n  .info p {\n    line-height: 1;\n  }\n  .info a {\n    color: inherit;\n    text-decoration: none;\n    font-weight: 400;\n  }\n  .info a:hover {\n    text-decoration: underline;\n  }\n  /*\n  Hack to remove background from Mobile Safari.\n  Can't use it globally since it destroys checkboxes in Firefox\n*/\n  @media screen and (-webkit-min-device-pixel-ratio:0) {\n    .toggle-all,\n    .todo-list li .toggle {\n      background: none;\n    }\n    .todo-list li .toggle {\n      height: 40px;\n    }\n  }\n  @media (max-width: 430px) {\n    .footer {\n      height: 50px;\n    }\n    .filters {\n      bottom: 10px;\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/src/views/dashboard/components/TodoList/index.vue",
    "content": "<template>\n  <section class=\"todoapp\">\n    <!-- header -->\n    <header class=\"header\">\n      <input class=\"new-todo\" autocomplete=\"off\" placeholder=\"Todo List\" @keyup.enter=\"addTodo\">\n    </header>\n    <!-- main section -->\n    <section v-show=\"todos.length\" class=\"main\">\n      <input id=\"toggle-all\" :checked=\"allChecked\" class=\"toggle-all\" type=\"checkbox\" @change=\"toggleAll({ done: !allChecked })\">\n      <label for=\"toggle-all\" />\n      <ul class=\"todo-list\">\n        <todo\n          v-for=\"(todo, index) in filteredTodos\"\n          :key=\"index\"\n          :todo=\"todo\"\n          @toggleTodo=\"toggleTodo\"\n          @editTodo=\"editTodo\"\n          @deleteTodo=\"deleteTodo\"\n        />\n      </ul>\n    </section>\n    <!-- footer -->\n    <footer v-show=\"todos.length\" class=\"footer\">\n      <span class=\"todo-count\">\n        <strong>{{ remaining }}</strong>\n        {{ remaining | pluralize('item') }} left\n      </span>\n      <ul class=\"filters\">\n        <li v-for=\"(val, key) in filters\" :key=\"key\">\n          <a :class=\"{ selected: visibility === key }\" @click.prevent=\"visibility = key\">{{ key | capitalize }}</a>\n        </li>\n      </ul>\n      <!-- <button class=\"clear-completed\" v-show=\"todos.length > remaining\" @click=\"clearCompleted\">\n        Clear completed\n      </button> -->\n    </footer>\n  </section>\n</template>\n\n<script>\nimport Todo from './Todo.vue'\n\nconst STORAGE_KEY = 'todos'\nconst filters = {\n  all: todos => todos,\n  active: todos => todos.filter(todo => !todo.done),\n  completed: todos => todos.filter(todo => todo.done)\n}\nconst defalutList = [\n  { text: 'star this repository', done: false },\n  { text: 'fork this repository', done: false },\n  { text: 'follow author', done: false },\n  { text: 'vue-element-admin', done: true },\n  { text: 'vue', done: true },\n  { text: 'element-ui', done: true },\n  { text: 'axios', done: true },\n  { text: 'webpack', done: true }\n]\nexport default {\n  components: { Todo },\n  filters: {\n    pluralize: (n, w) => n === 1 ? w : w + 's',\n    capitalize: s => s.charAt(0).toUpperCase() + s.slice(1)\n  },\n  data() {\n    return {\n      visibility: 'all',\n      filters,\n      // todos: JSON.parse(window.localStorage.getItem(STORAGE_KEY)) || defalutList\n      todos: defalutList\n    }\n  },\n  computed: {\n    allChecked() {\n      return this.todos.every(todo => todo.done)\n    },\n    filteredTodos() {\n      return filters[this.visibility](this.todos)\n    },\n    remaining() {\n      return this.todos.filter(todo => !todo.done).length\n    }\n  },\n  methods: {\n    setLocalStorage() {\n      window.localStorage.setItem(STORAGE_KEY, JSON.stringify(this.todos))\n    },\n    addTodo(e) {\n      const text = e.target.value\n      if (text.trim()) {\n        this.todos.push({\n          text,\n          done: false\n        })\n        this.setLocalStorage()\n      }\n      e.target.value = ''\n    },\n    toggleTodo(val) {\n      val.done = !val.done\n      this.setLocalStorage()\n    },\n    deleteTodo(todo) {\n      this.todos.splice(this.todos.indexOf(todo), 1)\n      this.setLocalStorage()\n    },\n    editTodo({ todo, value }) {\n      todo.text = value\n      this.setLocalStorage()\n    },\n    clearCompleted() {\n      this.todos = this.todos.filter(todo => !todo.done)\n      this.setLocalStorage()\n    },\n    toggleAll({ done }) {\n      this.todos.forEach(todo => {\n        todo.done = done\n        this.setLocalStorage()\n      })\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\">\n  @import './index.scss';\n</style>\n"
  },
  {
    "path": "frontend/src/views/dashboard/components/TransactionTable.vue",
    "content": "<template>\n  <el-table :data=\"list\" style=\"width: 100%;padding-top: 15px;\">\n    <el-table-column label=\"Order_No\">\n      <template slot-scope=\"{row}\">{{ row.order_no | orderNoFilter }}</template>\n    </el-table-column>\n    <el-table-column label=\"Price\" align=\"center\">\n      <template slot-scope=\"{row}\">¥{{ row.price | toThousandFilter }}</template>\n    </el-table-column>\n    <el-table-column label=\"Status\" align=\"center\">\n      <template slot-scope=\"{row}\">\n        <el-tag :type=\"row.status | statusFilter\">{{ row.status }}</el-tag>\n      </template>\n    </el-table-column>\n  </el-table>\n</template>\n\n<script>\nexport default {\n  filters: {\n    statusFilter(status) {\n      const statusMap = {\n        success: \"success\",\n        pending: \"danger\"\n      };\n      return statusMap[status];\n    },\n    orderNoFilter(str) {\n      return String(str).substring(0, 30);\n    }\n  },\n  data() {\n    return {\n      list: [\n        { order_no: 1, price: \"111\", status: \"success\" },\n        { order_no: 1, price: \"222\", status: \"pending\" },\n        { order_no: 2, price: \"333\", status: \"pending\" }\n      ]\n    };\n  },\n  created() {\n  },\n  methods: {}\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/dashboard/components/mixins/resize.js",
    "content": "import { debounce } from '@/utils'\n\nexport default {\n  data() {\n    return {\n      $_sidebarElm: null,\n      $_resizeHandler: null\n    }\n  },\n  mounted() {\n    this.$_resizeHandler = debounce(() => {\n      if (this.chart) {\n        this.chart.resize()\n      }\n    }, 100)\n    this.$_initResizeEvent()\n    this.$_initSidebarResizeEvent()\n  },\n  beforeDestroy() {\n    this.$_destroyResizeEvent()\n    this.$_destroySidebarResizeEvent()\n  },\n  // to fixed bug when cached by keep-alive\n  // https://github.com/PanJiaChen/vue-element-admin/issues/2116\n  activated() {\n    this.$_initResizeEvent()\n    this.$_initSidebarResizeEvent()\n  },\n  deactivated() {\n    this.$_destroyResizeEvent()\n    this.$_destroySidebarResizeEvent()\n  },\n  methods: {\n    // use $_ for mixins properties\n    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential\n    $_initResizeEvent() {\n      window.addEventListener('resize', this.$_resizeHandler)\n    },\n    $_destroyResizeEvent() {\n      window.removeEventListener('resize', this.$_resizeHandler)\n    },\n    $_sidebarResizeHandler(e) {\n      if (e.propertyName === 'width') {\n        this.$_resizeHandler()\n      }\n    },\n    $_initSidebarResizeEvent() {\n      this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]\n      this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)\n    },\n    $_destroySidebarResizeEvent() {\n      this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/src/views/dashboard/index.vue",
    "content": "<template>\n  <div class=\"dashboard-container\">\n    <div class=\"dashboard-editor-container\">\n      <panel-group @handleSetLineChartData=\"handleSetLineChartData\" />\n\n      <el-row style=\"background:#fff;padding:16px 16px 0;margin-bottom:32px;\">\n        <line-chart :chart-data=\"lineChartData\" />\n      </el-row>\n\n      <el-row :gutter=\"32\">\n        <el-col :xs=\"24\" :sm=\"24\" :lg=\"8\">\n          <div class=\"chart-wrapper\">\n            <raddar-chart />\n          </div>\n        </el-col>\n        <el-col :xs=\"24\" :sm=\"24\" :lg=\"8\">\n          <div class=\"chart-wrapper\">\n            <pie-chart />\n          </div>\n        </el-col>\n        <el-col :xs=\"24\" :sm=\"24\" :lg=\"8\">\n          <div class=\"chart-wrapper\">\n            <bar-chart />\n          </div>\n        </el-col>\n      </el-row>\n\n      <el-row :gutter=\"8\">\n        <el-col\n          :xs=\"{span: 24}\"\n          :sm=\"{span: 24}\"\n          :md=\"{span: 24}\"\n          :lg=\"{span: 8}\"\n          :xl=\"{span: 8}\"\n          style=\"padding-right:8px;margin-bottom:30px;\"\n        >\n          <transaction-table />\n        </el-col>\n        <el-col\n          :xs=\"{span: 24}\"\n          :sm=\"{span: 12}\"\n          :md=\"{span: 12}\"\n          :lg=\"{span: 8}\"\n          :xl=\"{span: 8}\"\n          style=\"margin-bottom:30px;\"\n        >\n          <todo-list />\n        </el-col>\n        <el-col\n          :xs=\"{span: 24}\"\n          :sm=\"{span: 12}\"\n          :md=\"{span: 12}\"\n          :lg=\"{span: 8}\"\n          :xl=\"{span: 8}\"\n          style=\"margin-bottom:30px;\"\n        >\n          <box-card />\n        </el-col>\n      </el-row>\n    </div>\n  </div>\n</template>\n\n<script>\nimport PanelGroup from \"./components/PanelGroup\";\nimport LineChart from \"./components/LineChart\";\nimport RaddarChart from \"./components/RaddarChart\";\nimport PieChart from \"./components/PieChart\";\nimport BarChart from \"./components/BarChart\";\nimport TransactionTable from \"./components/TransactionTable\";\nimport TodoList from \"./components/TodoList\";\nimport BoxCard from \"./components/BoxCard\";\n\nconst lineChartData = {\n  newVisitis: {\n    expectedData: [100, 120, 161, 134, 105, 160, 165],\n    actualData: [120, 82, 91, 154, 162, 140, 145]\n  },\n  messages: {\n    expectedData: [200, 192, 120, 144, 160, 130, 140],\n    actualData: [180, 160, 151, 106, 145, 150, 130]\n  },\n  purchases: {\n    expectedData: [80, 100, 121, 104, 105, 90, 100],\n    actualData: [120, 90, 100, 138, 142, 130, 130]\n  },\n  shoppings: {\n    expectedData: [130, 140, 141, 142, 145, 150, 160],\n    actualData: [120, 82, 91, 154, 162, 140, 130]\n  }\n};\n\nexport default {\n  name: \"DashboardAdmin\",\n  components: {\n    PanelGroup,\n    LineChart,\n    RaddarChart,\n    PieChart,\n    BarChart,\n    TransactionTable,\n    TodoList,\n    BoxCard\n  },\n  data() {\n    return {\n      lineChartData: lineChartData.newVisitis\n    };\n  },\n  methods: {\n    handleSetLineChartData(type) {\n      this.lineChartData = lineChartData[type];\n    }\n  }\n};\n</script>\n\n<style lang=\"scss\" scoped>\n.dashboard-editor-container {\n  padding: 32px;\n  background-color: rgb(240, 242, 245);\n  position: relative;\n\n  .github-corner {\n    position: absolute;\n    top: 0px;\n    border: 0;\n    right: 0;\n  }\n\n  .chart-wrapper {\n    background: #fff;\n    padding: 16px 16px 0;\n    margin-bottom: 32px;\n  }\n}\n\n@media (max-width: 1024px) {\n  .chart-wrapper {\n    padding: 8px;\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/error-page/401.vue",
    "content": "<template>\n  <div class=\"errPage-container\">\n    <el-button icon=\"el-icon-arrow-left\" class=\"pan-back-btn\" @click=\"back\">\n      返回\n    </el-button>\n    <el-row>\n      <el-col :span=\"12\">\n        <h1 class=\"text-jumbo text-ginormous\">\n          Oops!\n        </h1>\n        gif来源<a href=\"https://zh.airbnb.com/\" target=\"_blank\">airbnb</a> 页面\n        <h2>你没有权限去该页面</h2>\n        <h6>如有不满请联系你领导</h6>\n        <ul class=\"list-unstyled\">\n          <li>或者你可以去:</li>\n          <li class=\"link-type\">\n            <router-link to=\"/dashboard\">\n              回首页\n            </router-link>\n          </li>\n          <li class=\"link-type\">\n            <a href=\"https://www.taobao.com/\">随便看看</a>\n          </li>\n          <li><a href=\"#\" @click.prevent=\"dialogVisible=true\">点我看图</a></li>\n        </ul>\n      </el-col>\n      <el-col :span=\"12\">\n        <img :src=\"errGif\" width=\"313\" height=\"428\" alt=\"Girl has dropped her ice cream.\">\n      </el-col>\n    </el-row>\n    <el-dialog :visible.sync=\"dialogVisible\" title=\"随便看\">\n      <img :src=\"ewizardClap\" class=\"pan-img\">\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport errGif from '@/assets/401_images/401.gif'\n\nexport default {\n  name: 'Page401',\n  data() {\n    return {\n      errGif: errGif + '?' + +new Date(),\n      ewizardClap: 'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',\n      dialogVisible: false\n    }\n  },\n  methods: {\n    back() {\n      if (this.$route.query.noGoBack) {\n        this.$router.push({ path: '/dashboard' })\n      } else {\n        this.$router.go(-1)\n      }\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n  .errPage-container {\n    width: 800px;\n    max-width: 100%;\n    margin: 100px auto;\n    .pan-back-btn {\n      background: #008489;\n      color: #fff;\n      border: none!important;\n    }\n    .pan-gif {\n      margin: 0 auto;\n      display: block;\n    }\n    .pan-img {\n      display: block;\n      margin: 0 auto;\n      width: 100%;\n    }\n    .text-jumbo {\n      font-size: 60px;\n      font-weight: 700;\n      color: #484848;\n    }\n    .list-unstyled {\n      font-size: 14px;\n      li {\n        padding-bottom: 5px;\n      }\n      a {\n        color: #008489;\n        text-decoration: none;\n        &:hover {\n          text-decoration: underline;\n        }\n      }\n    }\n  }\n</style>\n"
  },
  {
    "path": "frontend/src/views/error-page/404.vue",
    "content": "<template>\n  <div class=\"wscn-http404-container\">\n    <div class=\"wscn-http404\">\n      <div class=\"pic-404\">\n        <img class=\"pic-404__parent\" src=\"@/assets/404_images/404.png\" alt=\"404\">\n        <img class=\"pic-404__child left\" src=\"@/assets/404_images/404_cloud.png\" alt=\"404\">\n        <img class=\"pic-404__child mid\" src=\"@/assets/404_images/404_cloud.png\" alt=\"404\">\n        <img class=\"pic-404__child right\" src=\"@/assets/404_images/404_cloud.png\" alt=\"404\">\n      </div>\n      <div class=\"bullshit\">\n        <div class=\"bullshit__oops\">OOPS!</div>\n        <div class=\"bullshit__info\">All rights reserved\n          <a style=\"color:#20a0ff\" href=\"https://wallstreetcn.com\" target=\"_blank\">wallstreetcn</a>\n        </div>\n        <div class=\"bullshit__headline\">{{ message }}</div>\n        <div class=\"bullshit__info\">Please check that the URL you entered is correct, or click the button below to return to the homepage.</div>\n        <a href=\"\" class=\"bullshit__return-home\">Back to home</a>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\n\nexport default {\n  name: 'Page404',\n  computed: {\n    message() {\n      return 'The webmaster said that you can not enter this page...'\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.wscn-http404-container{\n  transform: translate(-50%,-50%);\n  position: absolute;\n  top: 40%;\n  left: 50%;\n}\n.wscn-http404 {\n  position: relative;\n  width: 1200px;\n  padding: 0 50px;\n  overflow: hidden;\n  .pic-404 {\n    position: relative;\n    float: left;\n    width: 600px;\n    overflow: hidden;\n    &__parent {\n      width: 100%;\n    }\n    &__child {\n      position: absolute;\n      &.left {\n        width: 80px;\n        top: 17px;\n        left: 220px;\n        opacity: 0;\n        animation-name: cloudLeft;\n        animation-duration: 2s;\n        animation-timing-function: linear;\n        animation-fill-mode: forwards;\n        animation-delay: 1s;\n      }\n      &.mid {\n        width: 46px;\n        top: 10px;\n        left: 420px;\n        opacity: 0;\n        animation-name: cloudMid;\n        animation-duration: 2s;\n        animation-timing-function: linear;\n        animation-fill-mode: forwards;\n        animation-delay: 1.2s;\n      }\n      &.right {\n        width: 62px;\n        top: 100px;\n        left: 500px;\n        opacity: 0;\n        animation-name: cloudRight;\n        animation-duration: 2s;\n        animation-timing-function: linear;\n        animation-fill-mode: forwards;\n        animation-delay: 1s;\n      }\n      @keyframes cloudLeft {\n        0% {\n          top: 17px;\n          left: 220px;\n          opacity: 0;\n        }\n        20% {\n          top: 33px;\n          left: 188px;\n          opacity: 1;\n        }\n        80% {\n          top: 81px;\n          left: 92px;\n          opacity: 1;\n        }\n        100% {\n          top: 97px;\n          left: 60px;\n          opacity: 0;\n        }\n      }\n      @keyframes cloudMid {\n        0% {\n          top: 10px;\n          left: 420px;\n          opacity: 0;\n        }\n        20% {\n          top: 40px;\n          left: 360px;\n          opacity: 1;\n        }\n        70% {\n          top: 130px;\n          left: 180px;\n          opacity: 1;\n        }\n        100% {\n          top: 160px;\n          left: 120px;\n          opacity: 0;\n        }\n      }\n      @keyframes cloudRight {\n        0% {\n          top: 100px;\n          left: 500px;\n          opacity: 0;\n        }\n        20% {\n          top: 120px;\n          left: 460px;\n          opacity: 1;\n        }\n        80% {\n          top: 180px;\n          left: 340px;\n          opacity: 1;\n        }\n        100% {\n          top: 200px;\n          left: 300px;\n          opacity: 0;\n        }\n      }\n    }\n  }\n  .bullshit {\n    position: relative;\n    float: left;\n    width: 300px;\n    padding: 30px 0;\n    overflow: hidden;\n    &__oops {\n      font-size: 32px;\n      font-weight: bold;\n      line-height: 40px;\n      color: #1482f0;\n      opacity: 0;\n      margin-bottom: 20px;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-fill-mode: forwards;\n    }\n    &__headline {\n      font-size: 20px;\n      line-height: 24px;\n      color: #222;\n      font-weight: bold;\n      opacity: 0;\n      margin-bottom: 10px;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-delay: 0.1s;\n      animation-fill-mode: forwards;\n    }\n    &__info {\n      font-size: 13px;\n      line-height: 21px;\n      color: grey;\n      opacity: 0;\n      margin-bottom: 30px;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-delay: 0.2s;\n      animation-fill-mode: forwards;\n    }\n    &__return-home {\n      display: block;\n      float: left;\n      width: 110px;\n      height: 36px;\n      background: #1482f0;\n      border-radius: 100px;\n      text-align: center;\n      color: #ffffff;\n      opacity: 0;\n      font-size: 14px;\n      line-height: 36px;\n      cursor: pointer;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-delay: 0.3s;\n      animation-fill-mode: forwards;\n    }\n    @keyframes slideUp {\n      0% {\n        transform: translateY(60px);\n        opacity: 0;\n      }\n      100% {\n        transform: translateY(0);\n        opacity: 1;\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/icons/element-icons.js",
    "content": "const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'loading', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round']\n\nexport default elementIcons\n"
  },
  {
    "path": "frontend/src/views/icons/index.vue",
    "content": "<template>\n  <div class=\"icons-container\">\n    <aside>\n      <a href=\"https://panjiachen.github.io/vue-element-admin-site/guide/advanced/icon.html\" target=\"_blank\">Add and use\n      </a>\n    </aside>\n    <el-tabs type=\"border-card\">\n      <el-tab-pane label=\"Icons\">\n        <div class=\"grid\">\n          <div v-for=\"item of svgIcons\" :key=\"item\" @click=\"handleClipboard(generateIconCode(item),$event)\">\n            <el-tooltip placement=\"top\">\n              <div slot=\"content\">\n                {{ generateIconCode(item) }}\n              </div>\n              <div class=\"icon-item\">\n                <svg-icon :icon-class=\"item\" class-name=\"disabled\" />\n                <span>{{ item }}</span>\n              </div>\n            </el-tooltip>\n          </div>\n        </div>\n      </el-tab-pane>\n      <el-tab-pane label=\"Element-UI Icons\">\n        <div class=\"grid\">\n          <div v-for=\"item of elementIcons\" :key=\"item\" @click=\"handleClipboard(generateElementIconCode(item),$event)\">\n            <el-tooltip placement=\"top\">\n              <div slot=\"content\">\n                {{ generateElementIconCode(item) }}\n              </div>\n              <div class=\"icon-item\">\n                <i :class=\"'el-icon-' + item\" />\n                <span>{{ item }}</span>\n              </div>\n            </el-tooltip>\n          </div>\n        </div>\n      </el-tab-pane>\n    </el-tabs>\n  </div>\n</template>\n\n<script>\nimport clipboard from '@/utils/clipboard'\nimport svgIcons from './svg-icons'\nimport elementIcons from './element-icons'\n\nexport default {\n  name: 'Icons',\n  data() {\n    return {\n      svgIcons,\n      elementIcons\n    }\n  },\n  methods: {\n    generateIconCode(symbol) {\n      return `<svg-icon icon-class=\"${symbol}\" />`\n    },\n    generateElementIconCode(symbol) {\n      return `<i class=\"el-icon-${symbol}\" />`\n    },\n    handleClipboard(text, event) {\n      clipboard(text, event)\n    }\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.icons-container {\n  margin: 10px 20px 0;\n  overflow: hidden;\n\n  .grid {\n    position: relative;\n    display: grid;\n    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));\n  }\n\n  .icon-item {\n    margin: 20px;\n    height: 85px;\n    text-align: center;\n    width: 100px;\n    float: left;\n    font-size: 30px;\n    color: #24292e;\n    cursor: pointer;\n  }\n\n  span {\n    display: block;\n    font-size: 16px;\n    margin-top: 10px;\n  }\n\n  .disabled {\n    pointer-events: none;\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/icons/svg-icons.js",
    "content": "const req = require.context('../../icons/svg', false, /\\.svg$/)\nconst requireAll = requireContext => requireContext.keys()\n\nconst re = /\\.\\/(.*)\\.svg/\n\nconst svgIcons = requireAll(req).map(i => {\n  return i.match(re)[1]\n})\n\nexport default svgIcons\n"
  },
  {
    "path": "frontend/src/views/login/index.vue",
    "content": "<template>\n  <div class=\"login-container\">\n    <el-form\n      ref=\"loginForm\"\n      :model=\"loginForm\"\n      :rules=\"loginRules\"\n      class=\"login-form\"\n      auto-complete=\"on\"\n      label-position=\"left\"\n    >\n      <div class=\"title-container\">\n        <h3 class=\"title\">{{ $t('login.title') }}</h3>\n      </div>\n\n      <el-form-item prop=\"username\">\n        <span class=\"svg-container\">\n          <svg-icon icon-class=\"user\" />\n        </span>\n        <el-input\n          ref=\"username\"\n          v-model=\"loginForm.username\"\n          placeholder=\"账号\"\n          name=\"username\"\n          type=\"text\"\n          auto-complete=\"on\"\n        />\n      </el-form-item>\n\n      <el-form-item prop=\"password\">\n        <span class=\"svg-container\">\n          <svg-icon icon-class=\"password\" />\n        </span>\n        <el-input\n          :key=\"passwordType\"\n          ref=\"password\"\n          v-model=\"loginForm.password\"\n          :type=\"passwordType\"\n          placeholder=\"密码\"\n          name=\"password\"\n          auto-complete=\"on\"\n          @keyup.native=\"checkCapslock\"\n          @blur=\"capsTooltip = false\"\n          @keyup.enter.native=\"handleLogin\"\n        />\n        <span class=\"show-pwd\" @click=\"showPwd\">\n          <svg-icon :icon-class=\"passwordType === 'password' ? 'eye' : 'eye-open'\" />\n        </span>\n      </el-form-item>\n\n      <el-button\n        :loading=\"loading\"\n        type=\"primary\"\n        style=\"width:100%;margin-bottom:30px;\"\n        @click.native.prevent=\"handleLogin\"\n      >{{ $t('login.logIn') }}</el-button>\n    </el-form>\n  </div>\n</template>\n\n<script>\nimport { validUsername } from \"@/utils/validate\";\n\nexport default {\n  name: \"Login\",\n  components: {},\n  data() {\n    const validateUsername = (rule, value, callback) => {\n      if (!validUsername(value)) {\n        callback(new Error(\"Please enter the correct user name\"));\n      } else {\n        callback();\n      }\n    };\n    const validatePassword = (rule, value, callback) => {\n      if (value.length < 6) {\n        callback(new Error(\"The password can not be less than 6 digits\"));\n      } else {\n        callback();\n      }\n    };\n    return {\n      loginForm: {\n        username: \"\",\n        password: \"\"\n      },\n      loginRules: {\n        username: [\n          { required: true, trigger: \"blur\"}\n        ],\n        password: [\n          { required: true, trigger: \"blur\", validator: validatePassword }\n        ]\n      },\n      passwordType: \"password\",\n      capsTooltip: false,\n      loading: false,\n      showDialog: false,\n      redirect: undefined\n    };\n  },\n  watch: {\n    $route: {\n      handler: function(route) {\n        this.redirect = route.query && route.query.redirect;\n      },\n      immediate: true\n    }\n  },\n  created() {\n    // window.addEventListener('storage', this.afterQRScan)\n  },\n  mounted() {\n    if (this.loginForm.username === \"\") {\n      this.$refs.username.focus();\n    } else if (this.loginForm.password === \"\") {\n      this.$refs.password.focus();\n    }\n  },\n  methods: {\n    checkCapslock({ shiftKey, key } = {}) {\n      if (key && key.length === 1) {\n        if (\n          (shiftKey && key >= \"a\" && key <= \"z\") ||\n          (!shiftKey && key >= \"A\" && key <= \"Z\")\n        ) {\n          this.capsTooltip = true;\n        } else {\n          this.capsTooltip = false;\n        }\n      }\n      if (key === \"CapsLock\" && this.capsTooltip === true) {\n        this.capsTooltip = false;\n      }\n    },\n    showPwd() {\n      if (this.passwordType === \"password\") {\n        this.passwordType = \"\";\n      } else {\n        this.passwordType = \"password\";\n      }\n      this.$nextTick(() => {\n        this.$refs.password.focus();\n      });\n    },\n    handleLogin() {\n      this.$refs.loginForm.validate(valid => {\n        if (valid) {\n          this.loading = true;\n          this.$store\n            .dispatch(\"user/login\", this.loginForm)\n            .then(() => {\n              this.$router.push({ path: this.redirect || \"/\" }).catch(() => {\n                //如果不catch, 登录进去浏览器控制台会报错vue-router.esm.js:2051 Uncaught (in promise) undefined\n                //https://juejin.im/post/5d80d961f265da03ca11a1d9\n              });\n              this.loading = false;\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        } else {\n          console.log(\"error submit!!\");\n          return false;\n        }\n      });\n    }\n  }\n};\n</script>\n\n<style lang=\"scss\">\n/* 修复input 背景不协调 和光标变色 */\n/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */\n\n$bg: #283443;\n$light_gray: #fff;\n$cursor: #fff;\n\n@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {\n  .login-container .el-input input {\n    color: $cursor;\n  }\n}\n\n/* reset element-ui css */\n.login-container {\n  .el-input {\n    display: inline-block;\n    height: 47px;\n    width: 85%;\n\n    input {\n      background: transparent;\n      border: 0px;\n      -webkit-appearance: none;\n      border-radius: 0px;\n      padding: 12px 5px 12px 15px;\n      color: $light_gray;\n      height: 47px;\n      caret-color: $cursor;\n\n      &:-webkit-autofill {\n        box-shadow: 0 0 0px 1000px $bg inset !important;\n        -webkit-text-fill-color: $cursor !important;\n      }\n    }\n  }\n\n  .el-form-item {\n    background: rgba(0, 0, 0, 0.1);\n    border-radius: 5px;\n    color: #454545;\n  }\n  .choose-user {\n    text-align: center;\n    margin: 20px 0;\n  }\n}\n</style>\n\n<style lang=\"scss\" scoped>\n$bg: #2d3a4b;\n$dark_gray: #889aa4;\n$light_gray: #eee;\n\n.login-container {\n  min-height: 100%;\n  width: 100%;\n  background-color: $bg;\n  overflow: hidden;\n\n  .login-form {\n    position: relative;\n    width: 520px;\n    max-width: 100%;\n    padding: 160px 35px 0;\n    margin: 0 auto;\n    overflow: hidden;\n  }\n\n  .svg-container {\n    padding: 6px 5px 6px 15px;\n    color: $dark_gray;\n    vertical-align: middle;\n    width: 30px;\n    display: inline-block;\n  }\n\n  .title-container {\n    position: relative;\n\n    .title {\n      font-size: 26px;\n      color: $light_gray;\n      margin: 0px auto 40px auto;\n      text-align: center;\n      font-weight: bold;\n    }\n\n    .set-language {\n      color: #fff;\n      position: absolute;\n      top: 3px;\n      font-size: 18px;\n      right: 0px;\n      cursor: pointer;\n    }\n  }\n\n  .show-pwd {\n    position: absolute;\n    right: 10px;\n    top: 7px;\n    font-size: 16px;\n    color: $dark_gray;\n    cursor: pointer;\n    user-select: none;\n  }\n\n  @media only screen and (max-width: 470px) {\n    .thirdparty-button {\n      display: none;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/notice/mail.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-input\n        v-model=\"listQuery.search\"\n        placeholder=\"请输入内容\"\n        clearable\n        prefix-icon=\"el-icon-search\"\n        style=\"width: 200px;\"\n        class=\"filter-item\"\n        @keyup.enter.native=\"handleFilter\"\n        @clear=\"handleFilter\"\n      />\n      <el-button-group>\n        <el-button\n          class=\"filter-item\"\n          type=\"primary\"\n          icon=\"el-icon-search\"\n          @click=\"handleFilter\"\n        >{{ \"搜索\" }}</el-button>\n        <el-button\n          v-if=\"permissionList.add\"\n          class=\"filter-item\"\n          type=\"success\"\n          icon=\"el-icon-edit\"\n          @click=\"handleCreate\"\n        >{{ \"添加\" }}</el-button>\n      </el-button-group>\n    </div>\n\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      border\n      style=\"width: 100%\"\n      highlight-current-row\n      @sort-change=\"handleSortChange\"\n    >\n      <el-table-column label=\"名称\" prop=\"name\"></el-table-column>\n      <el-table-column label=\"主机\" prop=\"host\"></el-table-column>\n      <el-table-column label=\"账号\" prop=\"user\"></el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" width=\"260\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"{ row }\">\n          <el-button-group>\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"small\"\n              type=\"primary\"\n              @click=\"handleUpdate(row)\"\n            >{{ \"编辑\" }}</el-button>\n            <el-button\n              v-if=\"permissionList.del\"\n              size=\"small\"\n              type=\"danger\"\n              @click=\"handleDelete(row)\"\n            >{{ \"删除\" }}</el-button>\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <div class=\"table-pagination\">\n      <pagination\n        v-show=\"total > 0\"\n        :total=\"total\"\n        :page.sync=\"listQuery.offset\"\n        :limit.sync=\"listQuery.limit\"\n        @pagination=\"getList\"\n      />\n    </div>\n    <el-dialog\n      :title=\"textMap[dialogStatus]\"\n      :visible.sync=\"dialogFormVisible\"\n      :close-on-click-modal=\"false\"\n    >\n      <el-form\n        ref=\"dataForm\"\n        :rules=\"rules\"\n        :model=\"temp\"\n        label-position=\"left\"\n        label-width=\"80px\"\n        style=\"width: 400px; margin-left:50px;\"\n      >\n        <el-form-item label=\"名称\" prop=\"name\">\n          <el-input v-model=\"temp.name\" />\n        </el-form-item>\n        <el-form-item label=\"主机\" prop=\"host\">\n          <el-input v-model=\"temp.host\" />\n        </el-form-item>\n        <el-form-item label=\"账号\" prop=\"user\">\n          <el-input v-model=\"temp.user\" />\n        </el-form-item>\n        <el-form-item label=\"密码\" prop=\"password\">\n          <el-input v-model=\"temp.password\" />\n        </el-form-item>\n        <el-form-item label=\"接收者\" prop=\"to\">\n          <el-input v-model=\"temp.to\" />\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"memo\">\n          <el-input v-model=\"temp.memo\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogFormVisible = false\">{{ \"取消\" }}</el-button>\n        <el-button\n          type=\"primary\"\n          @click=\"dialogStatus === 'create' ? createData() : updateData()\"\n        >{{ \"确定\" }}</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { mail, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"mail\",\n\n  components: { Pagination },\n  data() {\n    return {\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      loading: true,\n      listQuery: {\n        offset: 1,\n        limit: 20,\n        search: undefined,\n        ordering: undefined\n      },\n      temp: {},\n      dialogFormVisible: false,\n      dialogStatus: \"\",\n      textMap: {\n        update: \"编辑\",\n        create: \"添加\"\n      },\n      rules: {\n        name: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }]\n      }\n    };\n  },\n  computed: {},\n  created() {\n    this.getMenuButton();\n    this.getList();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"mail\")\n        .then(response => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      mail.requestGet(this.listQuery).then(response => {\n        this.list = response.results;\n        this.total = response.count;\n        this.listLoading = false;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    },\n    resetTemp() {\n      this.temp = {\n        type: \"mail\",\n        name: \"\",\n        host: \"\",\n        user: \"\",\n        password: \"123456\",\n        to: \"\",\n        memo: \"\"\n      };\n    },\n    handleCreate() {\n      this.resetTemp();\n      this.dialogStatus = \"create\";\n      this.dialogFormVisible = true;\n      this.loading = false;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    createData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          mail\n            .requestPost(this.temp)\n            .then(response => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000\n              });\n              this.getList();\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleUpdate(row) {\n      this.temp = row;\n      this.dialogStatus = \"update\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    updateData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          mail\n            .requestPut(this.temp.id, this.temp)\n            .then(() => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000\n              });\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleDelete(row) {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\"\n      })\n        .then(() => {\n          mail.requestDelete(row.id).then(() => {\n            this.$message({\n              message: \"删除成功\",\n              type: \"success\"\n            });\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\"\n          });\n        });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/notice/telegram.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-input\n        v-model=\"listQuery.search\"\n        placeholder=\"请输入内容\"\n        clearable\n        prefix-icon=\"el-icon-search\"\n        style=\"width: 200px;\"\n        class=\"filter-item\"\n        @keyup.enter.native=\"handleFilter\"\n        @clear=\"handleFilter\"\n      />\n      <el-button-group>\n        <el-button\n          class=\"filter-item\"\n          type=\"primary\"\n          icon=\"el-icon-search\"\n          @click=\"handleFilter\"\n        >{{ \"搜索\" }}</el-button>\n        <el-button\n          v-if=\"permissionList.add\"\n          class=\"filter-item\"\n          type=\"success\"\n          icon=\"el-icon-edit\"\n          @click=\"handleCreate\"\n        >{{ \"添加\" }}</el-button>\n      </el-button-group>\n    </div>\n\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      border\n      style=\"width: 100%\"\n      highlight-current-row\n      @sort-change=\"handleSortChange\"\n    >\n      <el-table-column label=\"名称\" prop=\"name\"></el-table-column>\n      <el-table-column label=\"账号id\" prop=\"uid\"></el-table-column>\n      <el-table-column label=\"token\" prop=\"token\"></el-table-column>\n      <el-table-column label=\"chat_id\" prop=\"chat_id\"></el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" width=\"260\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"{ row }\">\n          <el-button-group>\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"small\"\n              type=\"primary\"\n              @click=\"handleUpdate(row)\"\n            >{{ \"编辑\" }}</el-button>\n            <el-button\n              v-if=\"permissionList.del\"\n              size=\"small\"\n              type=\"danger\"\n              @click=\"handleDelete(row)\"\n            >{{ \"删除\" }}</el-button>\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <div class=\"table-pagination\">\n      <pagination\n        v-show=\"total > 0\"\n        :total=\"total\"\n        :page.sync=\"listQuery.offset\"\n        :limit.sync=\"listQuery.limit\"\n        @pagination=\"getList\"\n      />\n    </div>\n    <el-dialog\n      :title=\"textMap[dialogStatus]\"\n      :visible.sync=\"dialogFormVisible\"\n      :close-on-click-modal=\"false\"\n    >\n      <el-form\n        ref=\"dataForm\"\n        :rules=\"rules\"\n        :model=\"temp\"\n        label-position=\"left\"\n        label-width=\"80px\"\n        style=\"width: 400px; margin-left:50px;\"\n      >\n        <el-form-item label=\"名称\" prop=\"name\">\n          <el-input v-model=\"temp.name\" />\n        </el-form-item>\n        <el-form-item label=\"账号id\" prop=\"uid\">\n          <el-input v-model=\"temp.uid\" />\n        </el-form-item>\n        <el-form-item label=\"token\" prop=\"token\">\n          <el-input v-model=\"temp.token\" />\n        </el-form-item>\n        <el-form-item label=\"chat_id\" prop=\"chat_id\">\n          <el-input v-model=\"temp.chat_id\" />\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"memo\">\n          <el-input v-model=\"temp.memo\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogFormVisible = false\">{{ \"取消\" }}</el-button>\n        <el-button\n          type=\"primary\"\n          @click=\"dialogStatus === 'create' ? createData() : updateData()\"\n        >{{ \"确定\" }}</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { telegram, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"telegram\",\n\n  components: { Pagination },\n  data() {\n    return {\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      loading: true,\n      listQuery: {\n        offset: 1,\n        limit: 20,\n        search: undefined,\n        ordering: undefined\n      },\n      temp: {},\n      dialogFormVisible: false,\n      dialogStatus: \"\",\n      textMap: {\n        update: \"编辑\",\n        create: \"添加\"\n      },\n      rules: {\n        name: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }]\n      }\n    };\n  },\n  computed: {},\n  created() {\n    this.getMenuButton();\n    this.getList();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"telegram\")\n        .then(response => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      telegram.requestGet(this.listQuery).then(response => {\n        this.list = response.results;\n        this.total = response.count;\n        this.listLoading = false;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    },\n    resetTemp() {\n      this.temp = {\n        type: \"telegram\",\n        name: \"\",\n        uid: \"\",\n        token: \"\",\n        chat_id: \"\",\n        memo: \"\"\n      };\n    },\n    handleCreate() {\n      this.resetTemp();\n      this.dialogStatus = \"create\";\n      this.dialogFormVisible = true;\n      this.loading = false;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    createData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          telegram\n            .requestPost(this.temp)\n            .then(response => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000\n              });\n              this.getList();\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleUpdate(row) {\n      this.temp = row;\n      this.dialogStatus = \"update\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    updateData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          telegram\n            .requestPut(this.temp.id, this.temp)\n            .then(() => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000\n              });\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleDelete(row) {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\"\n      })\n        .then(() => {\n          telegram.requestDelete(row.id).then(() => {\n            this.$message({\n              message: \"删除成功\",\n              type: \"success\"\n            });\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\"\n          });\n        });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/sys/group.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-input\n        v-model=\"listQuery.search\"\n        placeholder=\"请输入内容\"\n        clearable\n        prefix-icon=\"el-icon-search\"\n        style=\"width: 200px;\"\n        class=\"filter-item\"\n        @keyup.enter.native=\"handleFilter\"\n        @clear=\"handleFilter\"\n      />\n      <el-button-group>\n        <el-button\n          class=\"filter-item\"\n          type=\"primary\"\n          icon=\"el-icon-search\"\n          @click=\"handleFilter\"\n        >{{ \"搜索\" }}</el-button>\n        <el-button\n          v-if=\"permissionList.add\"\n          class=\"filter-item\"\n          type=\"success\"\n          icon=\"el-icon-edit\"\n          @click=\"handleCreate\"\n        >{{ \"添加\" }}</el-button>\n        <el-button\n          v-if=\"permissionList.del\"\n          :disabled=\"multipleSelection.length<1\"\n          class=\"filter-item\"\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          @click=\"handleBatchDel\"\n        >{{ \"删除\" }}</el-button>\n      </el-button-group>\n    </div>\n\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      border\n      style=\"width: 100%\"\n      highlight-current-row\n      @sort-change=\"handleSortChange\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"55\" />\n      <el-table-column label=\"名称\" prop=\"name\"></el-table-column>\n      <el-table-column label=\"排序\" prop=\"sequence\"></el-table-column>\n      <el-table-column label=\"包含用户\" prop=\"user_set\">\n        <template slot-scope=\"{ row }\">\n          <el-tag v-for=\"item in row.user_set\" :key=\"item.id\">{{item.username}}</el-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" width=\"260\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"{ row }\">\n          <el-button-group>\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"small\"\n              type=\"primary\"\n              @click=\"handleUpdate(row)\"\n            >{{ \"编辑\" }}</el-button>\n            <el-popconfirm title=\"你确定要删除吗\" @onConfirm=\"handleDelete(row)\">\n              <el-button\n                slot=\"reference\"\n                v-if=\"permissionList.del\"\n                size=\"small\"\n                type=\"danger\"\n              >{{ \"删除\" }}</el-button>\n            </el-popconfirm>\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <div class=\"table-pagination\">\n      <pagination\n        v-show=\"total > 0\"\n        :total=\"total\"\n        :page.sync=\"listQuery.offset\"\n        :limit.sync=\"listQuery.limit\"\n        @pagination=\"getList\"\n      />\n    </div>\n    <el-dialog\n      :title=\"textMap[dialogStatus]\"\n      :visible.sync=\"dialogFormVisible\"\n      :close-on-click-modal=\"false\"\n    >\n      <el-form\n        ref=\"dataForm\"\n        :rules=\"rules\"\n        :model=\"temp\"\n        label-position=\"left\"\n        label-width=\"80px\"\n        style=\"width: 400px; margin-left:50px;\"\n      >\n        <el-form-item label=\"父级\" prop=\"parent\">\n          <SelectTree\n            v-model.number=\"temp.parent\"\n            type=\"number\"\n            :props=\"propsSelectTree\"\n            :options=\"optionDataSelectTree2\"\n            :value=\"valueIdSelectTree2\"\n            :clearable=\"true\"\n            :accordion=\"true\"\n            @getValue=\"getSelectTreeValue($event, 2)\"\n          />\n        </el-form-item>\n        <el-form-item label=\"名称\" prop=\"name\">\n          <el-input v-model=\"temp.name\" />\n        </el-form-item>\n        <el-form-item label=\"代码\" prop=\"code\">\n          <el-input v-model=\"temp.code\" />\n        </el-form-item>\n        <el-form-item label=\"排序值\" prop=\"sequence\">\n          <el-input v-model=\"temp.sequence\" />\n        </el-form-item>\n        <el-form-item label=\"角色\" prop=\"roles\">\n          <el-tree\n            ref=\"tree\"\n            :check-strictly=\"false\"\n            :data=\"treeData\"\n            :props=\"treeProps\"\n            show-checkbox\n            accordion\n            default-expand-all\n            node-key=\"id\"\n            class=\"permission-tree\"\n          />\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"memo\">\n          <el-input v-model=\"temp.memo\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogFormVisible = false\">{{ \"取消\" }}</el-button>\n        <el-button\n          type=\"primary\"\n          @click=\"dialogStatus === 'create' ? createData() : updateData()\"\n        >{{ \"确定\" }}</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { group, menu, role, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport SelectTree from \"@/components/TreeSelect\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"group\",\n  components: { Pagination, SelectTree },\n  data() {\n    return {\n      valueIdSelectTree: null,\n      valueIdSelectTree2: null,\n      propsSelectTree: {\n        value: \"id\",\n        label: \"name\",\n        children: \"children\",\n        placeholder: \"父级\"\n      },\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      loading: true,\n      listQuery: {\n        page: 1,\n        limit: 20,\n        search: undefined,\n        ordering: undefined\n      },\n      temp: {},\n      dialogFormVisible: false,\n      dialogStatus: \"\",\n      textMap: {\n        update: \"编辑\",\n        create: \"添加\"\n      },\n      rules: {\n        name: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }],\n        code: [{ required: true, message: \"请输入代码\", trigger: \"blur\" }],\n        sequence: [{ required: true, message: \"请输入排序\", trigger: \"blur\" }]\n      },\n      multipleSelection: [],\n      allgroup: [],\n      treeProps: {\n        children: \"children\",\n        label: \"name\"\n      },\n      treeData: []\n    };\n  },\n  computed: {\n    optionDataSelectTree2() {\n      const cloneData = this.allgroup;\n      const ha = cloneData.filter(father => {\n        const branchArr = cloneData.filter(child => father.id === child.parent);\n        branchArr.length > 0 ? (father.children = branchArr) : null;\n        return father.parent === this.allgroup[0].parent;\n      });\n      return ha;\n    }\n  },\n  created() {\n    this.getMenuButton();\n    this.getList();\n    this.getAllgroup();\n    this.getTreeData();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"group\")\n        .then(response => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      group.requestGet(this.listQuery).then(response => {\n        this.list = response.results;\n        this.total = response.count;\n        this.listLoading = false;\n      });\n    },\n    getAllgroup() {\n      group.requestGet().then(response => {\n        this.allgroup = response.results;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    },\n    resetTemp() {\n      this.temp = {\n        parent: null,\n        name: \"\",\n        code: \"\",\n        sequence: \"\",\n        roles: [],\n        memo: \"\"\n      };\n    },\n    handleCreate() {\n      this.resetTemp();\n      this.dialogStatus = \"create\";\n      this.dialogFormVisible = true;\n      this.loading = false;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    createData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          this.temp.parent = this.valueIdSelectTree2;\n          this.temp.roles = this.$refs.tree.getCheckedKeys(true);\n          group\n            .requestPost(this.temp)\n            .then(response => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000\n              });\n              this.getList();\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleUpdate(row) {\n      this.temp = row;\n      this.dialogStatus = \"update\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n        this.valueIdSelectTree2 = this.temp.parent;\n        this.$refs.tree.setCheckedKeys(this.temp.roles);\n      });\n    },\n    updateData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          this.temp.parent = this.valueIdSelectTree2;\n          this.temp.roles = this.$refs.tree.getCheckedKeys(true);\n          group\n            .requestPut(this.temp.id, this.temp)\n            .then(() => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000\n              });\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleDelete(row) {\n      group.requestDelete(row.id).then(() => {\n        this.$message({\n          message: \"删除成功\",\n          type: \"success\"\n        });\n        this.getList();\n      });\n    },\n    getSelectTreeValue(value, type) {\n      if (type === 1) {\n        this.valueIdSelectTree = value;\n        this.handleFilter();\n      } else {\n        this.valueIdSelectTree2 = value;\n      }\n    },\n    handleSelectionChange(val) {\n      this.multipleSelection = val;\n    },\n    handleBatchDel() {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\"\n      })\n        .then(() => {\n          const ids = this.multipleSelection.map(x => x.id);\n          group.requestBulkDelete(ids).then(response => {\n            console.log(response.results);\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\"\n          });\n        });\n    },\n    getTreeData() {\n      role.requestGet().then(response => {\n        this.treeData = this.optionDataSelectTree(response.results);\n      });\n    },\n    optionDataSelectTree(data) {\n      const cloneData = data;\n      return cloneData.filter(father => {\n        const branchArr = cloneData.filter(child => father.id === child.parent);\n        branchArr.length > 0 ? (father.children = branchArr) : \"\";\n        return father.parent === data[0].parent;\n      });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/sys/menu.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-input\n        v-model=\"listQuery.search\"\n        placeholder=\"请输入内容\"\n        clearable\n        prefix-icon=\"el-icon-search\"\n        style=\"width: 200px;\"\n        class=\"filter-item\"\n        @keyup.enter.native=\"handleFilter\"\n        @clear=\"handleFilter\"\n      />\n      <el-button-group>\n        <el-button\n          class=\"filter-item\"\n          type=\"primary\"\n          icon=\"el-icon-search\"\n          @click=\"handleFilter\"\n        >{{ \"搜索\" }}</el-button>\n        <el-button\n          v-if=\"permissionList.add\"\n          class=\"filter-item\"\n          type=\"success\"\n          icon=\"el-icon-edit\"\n          @click=\"handleCreate\"\n        >{{ \"添加\" }}</el-button>\n        <el-button\n          v-if=\"permissionList.del\"\n          :disabled=\"multipleSelection.length<1\"\n          class=\"filter-item\"\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          @click=\"handleBatchDel\"\n        >{{ \"删除\" }}</el-button>\n      </el-button-group>\n    </div>\n\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      border\n      style=\"width: 100%\"\n      highlight-current-row\n      @sort-change=\"handleSortChange\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"55\" />\n      <el-table-column label=\"菜单名称\" prop=\"name\"></el-table-column>\n      <el-table-column label=\"菜单代码\" prop=\"code\"></el-table-column>\n      <el-table-column label=\"排序值\" prop=\"sequence\"></el-table-column>\n      <el-table-column label=\"菜单类型\" prop=\"type\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.type | menuTypeFilter }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作类型\" prop=\"operate\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.operate | operateTypeFilter }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"菜单状态\" prop=\"status\">\n        <template slot-scope=\"{ row }\">\n          <span>{{row.status}}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" width=\"260\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"{ row }\">\n          <el-button-group v-show=\"row.id !== 1\">\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"small\"\n              type=\"primary\"\n              @click=\"handleUpdate(row)\"\n            >{{ \"编辑\" }}</el-button>\n            <el-popconfirm title=\"你确定要删除吗\" @onConfirm=\"handleDelete(row)\">\n              <el-button\n                slot=\"reference\"\n                v-if=\"permissionList.del\"\n                size=\"small\"\n                type=\"danger\"\n              >{{ \"删除\" }}</el-button>\n            </el-popconfirm>\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <div class=\"table-pagination\">\n      <pagination\n        v-show=\"total > 0\"\n        :total=\"total\"\n        :page.sync=\"listQuery.page\"\n        :limit.sync=\"listQuery.limit\"\n        @pagination=\"getList\"\n      />\n    </div>\n    <el-dialog\n      :title=\"textMap[dialogStatus]\"\n      :visible.sync=\"dialogFormVisible\"\n      :close-on-click-modal=\"false\"\n    >\n      <el-form\n        ref=\"dataForm\"\n        :rules=\"rules\"\n        :model=\"temp\"\n        label-position=\"left\"\n        label-width=\"80px\"\n        style=\"width: 400px; margin-left:50px;\"\n      >\n        <el-form-item label=\"父级\" prop=\"parent\">\n          <SelectTree\n            v-model.number=\"temp.parent\"\n            type=\"number\"\n            :props=\"propsSelectTree\"\n            :options=\"optionDataSelectTree\"\n            :value=\"valueIdSelectTree2\"\n            :clearable=\"true\"\n            :accordion=\"true\"\n            @getValue=\"getSelectTreeValue($event, 2)\"\n          />\n        </el-form-item>\n        <el-form-item label=\"菜单名称\" prop=\"name\">\n          <el-input v-model=\"temp.name\" />\n        </el-form-item>\n        <el-form-item label=\"菜单代码\" prop=\"code\">\n          <el-input v-model=\"temp.code\" />\n        </el-form-item>\n        <el-form-item label=\"菜单URL\" prop=\"curl\">\n          <el-input v-model=\"temp.curl\" />\n        </el-form-item>\n        <el-form-item label=\"菜单图标\" prop=\"icon\">\n          <el-input v-model=\"temp.icon\" />\n        </el-form-item>\n        <el-form-item label=\"排序值\" prop=\"sequence\">\n          <el-input v-model=\"temp.sequence\" />\n        </el-form-item>\n        <el-form-item label=\"菜单类型\" prop=\"type\">\n          <el-select\n            v-model.number=\"temp.type\"\n            placeholder=\"状态\"\n            style=\"width: 90px\"\n            @change=\"handleshowOpera\"\n          >\n            <el-option\n              v-for=\"item in menuTypeOptions\"\n              :key=\"item.key\"\n              :label=\"item.display_name\"\n              :value=\"item.key\"\n            />\n          </el-select>\n        </el-form-item>\n        <el-form-item v-show=\"showOpera\" label=\"操作类型\" prop=\"operate\">\n          <el-select\n            v-model=\"temp.operate\"\n            placeholder=\"状态\"\n            style=\"width: 90px\"\n            class=\"filter-item\"\n          >\n            <el-option\n              v-for=\"item in operateTypeOptions\"\n              :key=\"item.key\"\n              :label=\"item.display_name\"\n              :value=\"item.key\"\n            />\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"菜单状态\" prop=\"status\">\n          <el-switch v-model=\"temp.status\" active-color=\"#13ce66\" inactive-color=\"#ff4949\"></el-switch>\n        </el-form-item>\n        <el-form-item label=\"是否缓存\" prop=\"no_cache\">\n          <el-switch v-model=\"temp.no_cache\" active-color=\"#13ce66\" inactive-color=\"#ff4949\"></el-switch>\n        </el-form-item>\n        <el-form-item label=\"隐藏菜单\" prop=\"hidden\">\n          <el-switch v-model=\"temp.hidden\" active-color=\"#13ce66\" inactive-color=\"#ff4949\"></el-switch>\n        </el-form-item>\n        <el-form-item label=\"激活菜单\" prop=\"active_menu\">\n          <el-input v-model=\"temp.active_menu\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogFormVisible = false\">{{ \"取消\" }}</el-button>\n        <el-button\n          type=\"primary\"\n          @click=\"dialogStatus === 'create' ? createData() : updateData()\"\n        >{{ \"确定\" }}</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { menu, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport SelectTree from \"@/components/TreeSelect\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"ymenu\",\n\n  components: { Pagination, SelectTree },\n  data() {\n    return {\n      valueIdSelectTree: 0,\n      valueIdSelectTree2: 0,\n      propsSelectTree: {\n        value: \"id\",\n        label: \"name\",\n        children: \"children\",\n        placeholder: \"父级\"\n      },\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      loading: true,\n      listQuery: {\n        page: 1,\n        limit: 20,\n        search: undefined,\n        ordering: undefined\n      },\n      temp: {},\n      dialogFormVisible: false,\n      dialogStatus: \"\",\n      textMap: {\n        update: \"编辑\",\n        create: \"添加\"\n      },\n      rules: {\n        name: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }],\n        sequence: [{ required: true, message: \"请输入排序\", trigger: \"blur\" }]\n      },\n      multipleSelection: [],\n      treeProps: {\n        children: \"children\",\n        label: \"name\"\n      },\n      treeData: [],\n      menuTypeOptions: [\n        { key: 1, display_name: \"模块\" },\n        { key: 2, display_name: \"菜单\" },\n        { key: 3, display_name: \"操作\" }\n      ],\n      operateTypeOptions: [\n        { key: \"none\", display_name: \"无\" },\n        { key: \"add\", display_name: \"新增\" },\n        { key: \"del\", display_name: \"删除\" },\n        { key: \"update\", display_name: \"编辑\" },\n        { key: \"view\", display_name: \"查看\" }\n      ],\n      showOpera: false,\n      allmean: []\n    };\n  },\n  computed: {\n    optionDataSelectTree() {\n      const cloneData = this.allmean;\n      return cloneData.filter(father => {\n        const branchArr = cloneData.filter(child => father.id === child.parent);\n        branchArr.length > 0 ? (father.children = branchArr) : \"\";\n        return father.parent === this.allmean[0].parent;\n      });\n    }\n  },\n  created() {\n    this.getMenuButton();\n    this.getList();\n    this.getAllMean();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"menu\")\n        .then(response => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      menu.requestGet(this.listQuery).then(response => {\n        this.list = response.results;\n        this.total = response.count;\n        this.listLoading = false;\n      });\n    },\n    getAllMean() {\n      menu.requestGet().then(response => {\n        this.allmean = response.results;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    },\n    resetTemp() {\n      this.temp = {\n        name: \"\",\n        code: \"\",\n        curl: \"\",\n        icon: \"list\",\n        sequence: \"\",\n        type: 2,\n        operate: \"none\",\n        status: true,\n        no_cache: true,\n        hidden: false,\n        active_menu: \"\",\n        parent: 0\n      };\n    },\n    handleCreate() {\n      this.resetTemp();\n      this.dialogStatus = \"create\";\n      this.dialogFormVisible = true;\n      this.loading = false;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    createData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          this.temp.parent = this.valueIdSelectTree2;\n          menu\n            .requestPost(this.temp)\n            .then(response => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000\n              });\n              this.getList();\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleUpdate(row) {\n      this.temp = row;\n      this.dialogStatus = \"update\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.valueIdSelectTree2 = this.temp.parent;\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    updateData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          this.temp.parent = this.valueIdSelectTree2;\n          menu\n            .requestPut(this.temp.id, this.temp)\n            .then(() => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000\n              });\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleDelete(row) {\n      menu.requestDelete(row.id).then(() => {\n        this.$message({\n          message: \"删除成功\",\n          type: \"success\"\n        });\n        this.getList();\n      });\n    },\n    handleshowOpera(val) {\n      if (val === 3) {\n        this.showOpera = true;\n      } else {\n        this.showOpera = false;\n        this.temp.operate = \"none\";\n      }\n    },\n    getSelectTreeValue(value, type) {\n      if (type === 1) {\n        this.valueIdSelectTree = value;\n        this.handleFilter();\n      } else {\n        this.valueIdSelectTree2 = value;\n      }\n    },\n    handleSelectionChange(val) {\n      this.multipleSelection = val;\n    },\n    handleBatchDel() {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\"\n      })\n        .then(() => {\n          const ids = this.multipleSelection.map(x => x.id);\n          menu.requestBulkDelete(ids).then(response => {\n            console.log(response.results);\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\"\n          });\n        });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/sys/role.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-input\n        v-model=\"listQuery.search\"\n        placeholder=\"请输入内容\"\n        clearable\n        prefix-icon=\"el-icon-search\"\n        style=\"width: 200px\"\n        class=\"filter-item\"\n        @keyup.enter.native=\"handleFilter\"\n        @clear=\"handleFilter\"\n      />\n      <el-button-group>\n        <el-button\n          class=\"filter-item\"\n          type=\"primary\"\n          icon=\"el-icon-search\"\n          @click=\"handleFilter\"\n          >{{ \"搜索\" }}</el-button\n        >\n        <el-button\n          v-if=\"permissionList.add\"\n          class=\"filter-item\"\n          type=\"success\"\n          icon=\"el-icon-edit\"\n          @click=\"handleCreate\"\n          >{{ \"添加\" }}</el-button\n        >\n        <el-button\n          v-if=\"permissionList.del\"\n          :disabled=\"multipleSelection.length < 1\"\n          class=\"filter-item\"\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          @click=\"handleBatchDel\"\n          >{{ \"删除\" }}</el-button\n        >\n      </el-button-group>\n    </div>\n\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      border\n      style=\"width: 100%\"\n      highlight-current-row\n      @sort-change=\"handleSortChange\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"55\" />\n      <el-table-column label=\"名称\" prop=\"name\"></el-table-column>\n      <el-table-column label=\"排序\" prop=\"sequence\"></el-table-column>\n      <el-table-column label=\"备注\" prop=\"memo\"></el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        width=\"320\"\n        class-name=\"small-padding fixed-width\"\n      >\n        <template slot-scope=\"{ row }\">\n          <el-button-group>\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"mini\"\n              type=\"primary\"\n              @click=\"handleUpdate('base', row)\"\n              >{{ \"编辑\" }}</el-button\n            >\n            <el-popconfirm title=\"你确定要删除吗\" @confirm=\"handleDelete(row)\">\n              <el-button\n                slot=\"reference\"\n                v-if=\"permissionList.del\"\n                size=\"mini\"\n                type=\"danger\"\n                >{{ \"删除\" }}</el-button\n              >\n            </el-popconfirm>\n          </el-button-group>\n          <el-button-group>\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"mini\"\n              type=\"success\"\n              plain\n              @click=\"handleUpdate('menu', row)\"\n              >{{ \"菜单\" }}</el-button\n            >\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"mini\"\n              type=\"warning\"\n              plain\n              @click=\"handleUpdate('perm', row)\"\n              >{{ \"权限\" }}</el-button\n            >\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <div class=\"table-pagination\">\n      <pagination\n        v-show=\"total > listQuery.limit\"\n        :total=\"total\"\n        :page.sync=\"listQuery.page\"\n        :limit.sync=\"listQuery.limit\"\n        @pagination=\"getList\"\n      />\n    </div>\n\n    <el-dialog\n      :title=\"textMap[dialogStatus]\"\n      :visible.sync=\"BaseFormVisible\"\n      :close-on-click-modal=\"false\"\n    >\n      <el-form\n        ref=\"dataForm\"\n        :rules=\"rules\"\n        :model=\"temp\"\n        label-position=\"left\"\n        label-width=\"80px\"\n      >\n        <div v-show=\"form == 'base'\">\n          <el-form-item label=\"父级\" prop=\"parent\">\n            <SelectTree\n              v-model.number=\"temp.parent\"\n              type=\"number\"\n              :props=\"propsSelectTree\"\n              :options=\"optionDataSelectTree2\"\n              :value=\"valueIdSelectTree2\"\n              :clearable=\"true\"\n              :accordion=\"true\"\n              @getValue=\"getSelectTreeValue($event, 2)\"\n            />\n          </el-form-item>\n          <el-form-item label=\"名称\" prop=\"name\">\n            <el-input v-model=\"temp.name\" />\n          </el-form-item>\n          <el-form-item label=\"代码\" prop=\"code\">\n            <el-input v-model=\"temp.code\" />\n          </el-form-item>\n          <el-form-item label=\"排序值\" prop=\"sequence\">\n            <el-input v-model=\"temp.sequence\" />\n          </el-form-item>\n          <el-form-item label=\"备注\" prop=\"memo\">\n            <el-input v-model=\"temp.memo\" />\n          </el-form-item>\n        </div>\n        <div v-show=\"form == 'menu'\">\n          <el-form-item label=\"菜单\" prop=\"menus\">\n            <el-tree\n              ref=\"tree\"\n              :check-strictly=\"false\"\n              :data=\"treeData\"\n              :props=\"treeProps\"\n              show-checkbox\n              :default-expanded-keys=\"[1]\"\n              :accordion=\"true\"\n              node-key=\"id\"\n              class=\"permission-tree\"\n            />\n          </el-form-item>\n        </div>\n        <div v-show=\"form == 'perm'\">\n          <el-form-item label=\"模块权限\" prop=\"model_perms\">\n            <el-transfer\n              v-model=\"temp.model_perms\"\n              filterable\n              :titles=\"['未选择', '已选择']\"\n              :data=\"allperm\"\n              :props=\"permprops\"\n            ></el-transfer>\n          </el-form-item>\n        </div>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"BaseFormVisible = false\">{{ \"取消\" }}</el-button>\n        <el-button\n          type=\"primary\"\n          @click=\"dialogStatus === 'create' ? createData() : updateData()\"\n          >{{ \"确定\" }}</el-button\n        >\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { role, menu, perm, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport SelectTree from \"@/components/TreeSelect\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate,\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"role\",\n  components: { Pagination, SelectTree },\n  data() {\n    return {\n      valueIdSelectTree: 0,\n      valueIdSelectTree2: 0,\n      propsSelectTree: {\n        value: \"id\",\n        label: \"name\",\n        children: \"children\",\n        placeholder: \"父级\",\n      },\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false,\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      loading: true,\n      listQuery: {\n        page: 1,\n        limit: 20,\n        search: undefined,\n        ordering: undefined,\n      },\n      form: \"base\",\n      temp: {},\n      BaseFormVisible: false,\n      dialogStatus: \"\",\n      textMap: {\n        update: \"编辑\",\n        create: \"添加\",\n      },\n      rules: {\n        name: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }],\n        code: [{ required: true, message: \"请输入代码\", trigger: \"blur\" }],\n        sequence: [{ required: true, message: \"请输入排序\", trigger: \"blur\" }],\n      },\n      multipleSelection: [],\n      treeProps: {\n        children: \"children\",\n        label: \"name\",\n      },\n      treeData: [],\n      allrole: [],\n      allperm: [],\n      permprops: {\n        key: \"id\",\n        label: \"name\",\n      },\n    };\n  },\n  computed: {\n    optionDataSelectTree2() {\n      const cloneData = this.allrole;\n      const ha = cloneData.filter((father) => {\n        const branchArr = cloneData.filter(\n          (child) => father.id === child.parent\n        );\n        branchArr.length > 0 ? (father.children = branchArr) : \"\";\n        return father.parent === this.allrole[0].parent;\n      });\n      return ha;\n    },\n  },\n  created() {\n    this.getMenuButton();\n    this.getList();\n    this.getTreeData();\n    this.getAllRole();\n    this.getAllPerm();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"role\")\n        .then((response) => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      role.requestGet(this.listQuery).then((response) => {\n        this.list = response.results;\n        this.total = response.count;\n        this.listLoading = false;\n      });\n    },\n    getAllRole() {\n      role.requestGet().then((response) => {\n        this.allrole = response.results;\n      });\n    },\n    getAllPerm() {\n      perm.requestGet().then((response) => {\n        this.allperm = response.results;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    },\n    resetTemp() {\n      this.temp = {\n        name: \"\",\n        code: \"\",\n        sequence: \"\",\n        menus: [],\n        model_perms: [],\n        memo: \"\",\n      };\n    },\n    handleCreate() {\n      this.resetTemp();\n      this.dialogStatus = \"create\";\n      this.BaseFormVisible = true;\n      this.loading = false;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    createData() {\n      this.$refs[\"dataForm\"].validate((valid) => {\n        if (valid) {\n          this.loading = true;\n          this.temp.parent = this.valueIdSelectTree2;\n          this.temp.menus = this.$refs.tree.getCheckedKeys();\n          role\n            .requestPost(this.temp)\n            .then((response) => {\n              this.BaseFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000,\n              });\n              this.getList();\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleUpdate(val, row) {\n      this.temp = row;\n      this.dialogStatus = \"update\";\n      this.form = val;\n      this.BaseFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n        this.valueIdSelectTree2 = this.temp.parent;\n        this.$refs.tree.setCheckedKeys(this.temp.menus);\n      });\n    },\n    updateData() {\n      this.$refs[\"dataForm\"].validate((valid) => {\n        if (valid) {\n          this.loading = true;\n          if (this.form == \"menu\") {\n            this.temp.parent = this.valueIdSelectTree2;\n            this.temp.menus = this.$refs.tree.getCheckedKeys();\n          }\n          role\n            .requestPut(this.temp.id, this.temp)\n            .then(() => {\n              this.BaseFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000,\n              });\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleDelete(row) {\n      role.requestDelete(row.id).then(() => {\n        this.$message({\n          message: \"删除成功\",\n          type: \"success\",\n        });\n        this.getList();\n      });\n    },\n    getSelectTreeValue(value, type) {\n      if (type === 1) {\n        this.valueIdSelectTree = value;\n        this.handleFilter();\n      } else {\n        this.valueIdSelectTree2 = value;\n      }\n    },\n    handleSelectionChange(val) {\n      this.multipleSelection = val;\n    },\n    handleBatchDel() {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\",\n      })\n        .then(() => {\n          const ids = this.multipleSelection.map((x) => x.id);\n          role.requestBulkDelete(ids).then((response) => {\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\",\n          });\n        });\n    },\n    getTreeData() {\n      menu.requestGet().then((response) => {\n        this.treeData = this.optionDataSelectTree(response.results);\n      });\n    },\n    optionDataSelectTree(data) {\n      const cloneData = data;\n      return cloneData.filter((father) => {\n        const branchArr = cloneData.filter(\n          (child) => father.id === child.parent\n        );\n        branchArr.length > 0 ? (father.children = branchArr) : \"\";\n        return father.parent === data[0].parent;\n      });\n    },\n  },\n};\n</script>"
  },
  {
    "path": "frontend/src/views/sys/user.vue",
    "content": "  <template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-input\n        v-model=\"listQuery.search\"\n        placeholder=\"请输入内容\"\n        clearable\n        prefix-icon=\"el-icon-search\"\n        style=\"width: 200px;\"\n        class=\"filter-item\"\n        @keyup.enter.native=\"handleFilter\"\n        @clear=\"handleFilter\"\n      />\n      <el-button-group>\n        <el-button\n          class=\"filter-item\"\n          type=\"primary\"\n          icon=\"el-icon-search\"\n          @click=\"handleFilter\"\n        >{{ \"搜索\" }}</el-button>\n        <el-button\n          v-if=\"permissionList.add\"\n          class=\"filter-item\"\n          type=\"success\"\n          icon=\"el-icon-edit\"\n          @click=\"handleCreate\"\n        >{{ \"添加\" }}</el-button>\n        <el-button\n          v-if=\"permissionList.del\"\n          :disabled=\"multipleSelection.length<1\"\n          class=\"filter-item\"\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          @click=\"handleBatchDel\"\n        >{{ \"删除\" }}</el-button>\n      </el-button-group>\n    </div>\n\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      border\n      style=\"width: 100%\"\n      highlight-current-row\n      @sort-change=\"handleSortChange\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"55\" />\n      <el-table-column label=\"用户名\" prop=\"username\"></el-table-column>\n      <el-table-column label=\"真实姓名\" prop=\"realname\"></el-table-column>\n      <el-table-column label=\"头像\" align=\"center\">\n        <template slot-scope=\"{ row }\">\n          <el-popover placement=\"top\" width=\"200\" trigger=\"hover\">\n            <el-image :src=\"row.avatar\" fit=\"cover\"></el-image>\n            <el-avatar slot=\"reference\" :src=\"row.avatar\"></el-avatar>\n          </el-popover>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"状态\" prop=\"status\" sortable=\"custom\">\n        <template slot-scope=\"{ row }\">\n          <el-tag v-if=\"row.status\" type=\"success\">启用</el-tag>\n          <el-tag v-else type=\"danger\">禁用</el-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" width=\"260\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"{ row }\">\n          <el-button-group v-show=\"!row.is_admin\">\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"small\"\n              type=\"primary\"\n              @click=\"handleUpdate(row)\"\n            >{{ \"编辑\" }}</el-button>\n            <el-popconfirm title=\"你确定要删除吗\" @onConfirm=\"handleDelete(row)\">\n              <el-button\n                slot=\"reference\"\n                v-if=\"permissionList.del\"\n                size=\"small\"\n                type=\"danger\"\n              >{{ \"删除\" }}</el-button>\n            </el-popconfirm>\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <div class=\"table-pagination\">\n      <pagination\n        v-show=\"total > 0\"\n        :total=\"total\"\n        :page.sync=\"listQuery.page\"\n        :limit.sync=\"listQuery.limit\"\n        @pagination=\"getList\"\n      />\n    </div>\n    <el-dialog\n      :title=\"textMap[dialogStatus]\"\n      :visible.sync=\"dialogFormVisible\"\n      :close-on-click-modal=\"false\"\n    >\n      <el-form\n        ref=\"dataForm\"\n        :rules=\"rules\"\n        :model=\"temp\"\n        label-position=\"left\"\n        label-width=\"80px\"\n        style=\"width: 400px; margin-left:50px;\"\n      >\n        <el-form-item label=\"用户名\" prop=\"username\">\n          <el-input v-model=\"temp.username\" :disabled=\"dialogStatus === 'create' ? false : true\" />\n        </el-form-item>\n        <el-form-item v-if=\"dialogStatus === 'create' ? true : false\" label=\"密码\" prop=\"password\">\n          <el-input\n            v-model=\"temp.password\"\n            placeholder=\"6-20位\"\n            show-password\n            minlength=\"6\"\n            maxlength=\"20\"\n          />\n        </el-form-item>\n        <el-form-item label=\"真实姓名\" prop=\"realname\">\n          <el-input v-model=\"temp.realname\" />\n        </el-form-item>\n        <el-form-item label=\"分组\" prop=\"realname\">\n          <SelectTree\n            v-model.number=\"temp.group\"\n            type=\"number\"\n            :props=\"propsSelectTree\"\n            :options=\"optionDataSelectTree2\"\n            :value=\"valueIdSelectTree2\"\n            :clearable=\"true\"\n            :accordion=\"true\"\n            @getValue=\"getSelectTreeValue($event, 2)\"\n          />\n        </el-form-item>\n        <el-form-item label=\"头像\" prop=\"avatar\">\n          <el-input v-model=\"temp.avatar\" />\n        </el-form-item>\n        <el-form-item label=\"介绍\" prop=\"memo\">\n          <el-input v-model=\"temp.memo\" />\n        </el-form-item>\n        <el-form-item label=\"状态\" prop=\"status\">\n          <el-switch v-model=\"temp.status\" active-color=\"#13ce66\" inactive-color=\"#ff4949\"></el-switch>\n        </el-form-item>\n        <el-form-item label=\"角色\" prop=\"roles\">\n          <el-tree\n            ref=\"tree\"\n            :check-strictly=\"false\"\n            :data=\"treeData\"\n            :props=\"treeProps\"\n            show-checkbox\n            accordion\n            default-expand-all\n            node-key=\"id\"\n            class=\"permission-tree\"\n          />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogFormVisible = false\">{{ \"取消\" }}</el-button>\n        <el-button\n          type=\"primary\"\n          @click=\"dialogStatus === 'create' ? createData() : updateData()\"\n        >{{ \"确定\" }}</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { user, group, role, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport SelectTree from \"@/components/TreeSelect\";\n\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"user\",\n  components: { Pagination, SelectTree },\n  data() {\n    return {\n      valueIdSelectTree: 0,\n      valueIdSelectTree2: 0,\n      propsSelectTree: {\n        value: \"id\",\n        label: \"name\",\n        children: \"children\",\n        placeholder: \"父级\"\n      },\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      loading: true,\n      listQuery: {\n        page: 1,\n        limit: 20,\n        search: undefined,\n        ordering: undefined\n      },\n      temp: {},\n      dialogFormVisible: false,\n      dialogStatus: \"\",\n      textMap: {\n        update: \"编辑\",\n        create: \"添加\"\n      },\n      rules: {\n        username: [\n          { required: true, message: \"请输入用户名\", trigger: \"blur\" }\n        ],\n        password: [\n          {\n            min: 6,\n            max: 20,\n            required: true,\n            message: \"长度在 6 到 20 个字符\",\n            trigger: \"blur\"\n          }\n        ]\n      },\n      multipleSelection: [],\n      treeProps: {\n        children: \"children\",\n        label: \"name\"\n      },\n      treeData: [],\n      allgroup: []\n    };\n  },\n  computed: {\n    optionDataSelectTree2() {\n      const cloneData = this.allgroup;\n      const ha = cloneData.filter(father => {\n        const branchArr = cloneData.filter(child => father.id === child.parent);\n        branchArr.length > 0 ? (father.children = branchArr) : \"\";\n        return father.parent === this.allgroup[0].parent;\n      });\n      return ha;\n    }\n  },\n  created() {\n    this.getMenuButton();\n    this.getList();\n    this.getGroupList();\n    this.getTreeData();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"user\")\n        .then(response => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      user.requestGet(this.listQuery).then(response => {\n        this.list = response.results;\n        this.total = response.count;\n        this.listLoading = false;\n      });\n    },\n    getGroupList() {\n      group.requestGet().then(response => {\n        this.allgroup = response.results;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    },\n    resetTemp() {\n      this.temp = {\n        username: \"\",\n        password: \"\",\n        realname: \"\",\n        group: \"\",\n        avatar:\n          \"http://m.imeitou.com/uploads/allimg/2017110610/b3c433vwhsk.jpg\",\n        roles: [],\n        status: true,\n        memo: \"\"\n      };\n    },\n    handleCreate() {\n      this.resetTemp();\n      this.dialogStatus = \"create\";\n      this.dialogFormVisible = true;\n      this.loading = false;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    createData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          this.temp.group = this.valueIdSelectTree2;\n          this.temp.roles = this.$refs.tree.getCheckedKeys(true);\n          user\n            .requestPost(this.temp)\n            .then(response => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000\n              });\n              this.getList();\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleUpdate(row) {\n      // this.temp = Object.assign({},row, {\n      //   group: row.group.id,\n      //   roles: row.roles.map(a => a.id),\n      // });\n      this.temp = row;\n      this.dialogStatus = \"update\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n        this.valueIdSelectTree2 = this.temp.group;\n        this.$refs.tree.setCheckedKeys(this.temp.roles);\n      });\n    },\n    updateData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          this.temp.group = this.valueIdSelectTree2;\n          this.temp.roles = this.$refs.tree.getCheckedKeys(true);\n          user\n            .requestPut(this.temp.id, this.temp)\n            .then(() => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000\n              });\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleDelete(row) {\n      user.requestDelete(row.id).then(() => {\n        this.$message({\n          message: \"删除成功\",\n          type: \"success\"\n        });\n        this.getList();\n      });\n    },\n    getSelectTreeValue(value, type) {\n      if (type === 1) {\n        this.valueIdSelectTree = value;\n        this.handleFilter();\n      } else {\n        this.valueIdSelectTree2 = value;\n      }\n    },\n    handleSelectionChange(val) {\n      this.multipleSelection = val;\n    },\n    handleBatchDel() {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\"\n      })\n        .then(() => {\n          const ids = this.multipleSelection.map(x => x.id);\n          user.requestBulkDelete(ids).then(response => {\n            console.log(response.results);\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\"\n          });\n        });\n    },\n    getTreeData() {\n      role.requestGet().then(response => {\n        this.treeData = this.optionDataSelectTree(response.results);\n      });\n    },\n    optionDataSelectTree(data) {\n      const cloneData = data;\n      return cloneData.filter(father => {\n        const branchArr = cloneData.filter(child => father.id === child.parent);\n        branchArr.length > 0 ? (father.children = branchArr) : \"\";\n        return father.parent === data[0].parent;\n      });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/ticket/all_ticket.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-input\n        v-model=\"listQuery.search\"\n        placeholder=\"请输入内容\"\n        clearable\n        prefix-icon=\"el-icon-search\"\n        style=\"width: 200px\"\n        class=\"filter-item\"\n        @keyup.enter.native=\"handleFilter\"\n        @clear=\"handleFilter\"\n      />\n      <el-button-group>\n        <el-button\n          class=\"filter-item\"\n          type=\"primary\"\n          icon=\"el-icon-search\"\n          @click=\"handleFilter\"\n          >{{ \"搜索\" }}</el-button\n        >\n        <!-- <el-button\n          v-if=\"permissionList.del & (username === 'admin')\"\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          @click=\"handleBatchDel\"\n          >{{ \"删除\" }}</el-button\n        > -->\n      </el-button-group>\n      <el-radio-group\n        class=\"filter-item\"\n        text-color=\"#db5a6b\"\n        fill=\"#ffc773\"\n        v-model=\"listQuery.transition__attribute_type\"\n        @change=\"getList\"\n      >\n        <el-radio-button\n          v-for=\"(item, index) in attribute_types\"\n          :key=\"index\"\n          :label=\"index\"\n        >{{item}}</el-radio-button>\n      </el-radio-group>\n    </div>\n\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      border\n      style=\"width: 100%\"\n      highlight-current-row\n      @sort-change=\"handleSortChange\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column\n        v-if=\"username === 'admin'\"\n        type=\"selection\"\n        width=\"55\"\n      />\n      <el-table-column label=\"名称\" prop=\"name\"></el-table-column>\n      <el-table-column label=\"工单流水号\" prop=\"sn\" width=\"240\">\n        <template slot-scope=\"{ row }\">\n          <router-link :to=\"'/s_ticket/' + row.id\">\n            <el-link type=\"success\">{{ row.sn }}</el-link>\n          </router-link>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"申请人\" prop=\"create_user\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.create_user.username }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"当前环节\" prop=\"state\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.state.name }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"当前状态\" prop=\"transition\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.transition.attribute_type | AttributeTypeFilter }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"当前处理人\" prop=\"participant\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.participant }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" prop=\"create_time\"></el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        class-name=\"small-padding fixed-width\"\n      >\n        <template slot-scope=\"{ row }\">\n          <el-button-group>\n            <el-button\n              v-if=\"permissionList.del & (username === 'admin')\"\n              size=\"small\"\n              type=\"danger\"\n              @click=\"handleDelete(row)\"\n              >{{ \"删除\" }}</el-button\n            >\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <div class=\"table-pagination\">\n      <pagination\n        v-show=\"total > 0\"\n        :total=\"total\"\n        :page.sync=\"listQuery.page\"\n        :limit.sync=\"listQuery.limit\"\n        @pagination=\"getList\"\n      />\n    </div>\n  </div>\n</template>\n\n<script>\nimport { ticket, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate,\n} from \"@/utils/permission\";\nimport { mapGetters } from \"vuex\";\n\nexport default {\n  name: \"all_ticket\",\n\n  components: { Pagination },\n  data() {\n    return {\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false,\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      loading: true,\n      listQuery: {\n        page: 1,\n        limit: 20,\n        search: undefined,\n        ordering: undefined,\n        transition__attribute_type: \"\",\n      },\n      multipleSelection: [],\n      attribute_types: {\n        '': \"全部\",\n        0: \"草稿\",\n        1: \"待审\",\n        2: \"驳回\",\n        3: \"撤销\",\n        4: \"结束\",\n        5: \"已关闭\",\n      },\n    };\n  },\n  computed: {\n    ...mapGetters([\"username\"]),\n  },\n  created() {\n    this.getMenuButton();\n    this.getList();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"all_ticket\")\n        .then((response) => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      ticket.requestGet(this.listQuery).then((response) => {\n        this.list = response.results;\n        this.total = response.count;\n        this.listLoading = false;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    },\n    handleDelete(row) {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\",\n      })\n        .then(() => {\n          ticket.requestDelete(row.id).then(() => {\n            this.$message({\n              message: \"删除成功\",\n              type: \"success\",\n            });\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\",\n          });\n        });\n    },\n    handleSelectionChange(val) {\n      this.multipleSelection = val;\n    },\n    handleBatchDel() {\n      if (this.multipleSelection.length === 0) {\n        this.$message({\n          message: \"未选中任何行\",\n          type: \"warning\",\n          duration: 2000,\n        });\n        return;\n      }\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\",\n      })\n        .then(() => {\n          const ids = this.multipleSelection.map((x) => x.id);\n          ticket.requestBulkDelete(ids).then((response) => {\n            console.log(response.results);\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\",\n          });\n        });\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/ticket/my_ticket.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-input\n        v-model=\"listQuery.search\"\n        placeholder=\"请输入内容\"\n        clearable\n        prefix-icon=\"el-icon-search\"\n        style=\"width: 200px;\"\n        class=\"filter-item\"\n        @keyup.enter.native=\"handleFilter\"\n        @clear=\"handleFilter\"\n      />\n      <el-button-group>\n        <el-button\n          class=\"filter-item\"\n          type=\"primary\"\n          icon=\"el-icon-search\"\n          @click=\"handleFilter\"\n        >{{ \"搜索\" }}</el-button>\n      </el-button-group>\n    </div>\n\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      border\n      style=\"width: 100%\"\n      highlight-current-row\n      @sort-change=\"handleSortChange\"\n    >\n      <el-table-column label=\"名称\" prop=\"name\"></el-table-column>\n      <el-table-column label=\"工单流水号\" prop=\"sn\" width=\"240\">\n        <template slot-scope=\"{ row }\">\n          <router-link :to=\"'/s_ticket/' + row.id\">\n            <el-link type=\"success\">{{row.sn}}</el-link>\n          </router-link>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"申请人\" prop=\"create_user\">\n        <template slot-scope=\"{ row }\">\n          <span>{{row.create_user.username}}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"当前环节\" prop=\"state\">\n        <template slot-scope=\"{ row }\">\n          <span>{{row.state.name}}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"当前状态\" prop=\"transition\">\n        <template slot-scope=\"{ row }\">\n          <span>{{row.transition.attribute_type|AttributeTypeFilter}}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"当前处理人\" prop=\"participant\">\n        <template slot-scope=\"{ row }\">\n          <span>{{row.participant}}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" prop=\"create_time\"></el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"{ row }\">\n          <el-button-group>\n            <el-button\n              v-if=\"permissionList.del && row.state.order_id < 3\"\n              size=\"small\"\n              type=\"danger\"\n              @click=\"handleDelete(row)\"\n            >{{ \"删除\" }}</el-button>\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <div class=\"table-pagination\">\n      <pagination\n        v-show=\"total > 0\"\n        :total=\"total\"\n        :page.sync=\"listQuery.page\"\n        :limit.sync=\"listQuery.limit\"\n        @pagination=\"getList\"\n      />\n    </div>\n  </div>\n</template>\n\n<script>\nimport { ticket, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate\n} from \"@/utils/permission\";\nimport { mapGetters } from \"vuex\";\n\nexport default {\n  name: \"my_ticket\",\n\n  components: { Pagination },\n  data() {\n    return {\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      loading: true,\n      listQuery: {\n        page: 1,\n        limit: 20,\n        search: undefined,\n        ordering: undefined,\n        create_user__username: this.username\n      }\n    };\n  },\n  computed: {\n    ...mapGetters([\"username\"])\n  },\n  created() {\n    this.getMenuButton();\n    this.getList();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"my_ticket\")\n        .then(response => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      this.listQuery.create_user__username =  this.username\n      ticket.requestGet(this.listQuery).then(response => {\n        this.list = response.results;\n        this.total = response.count;\n        this.listLoading = false;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    },\n    handleDelete(row) {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\"\n      })\n        .then(() => {\n          ticket.requestDelete(row.id).then(() => {\n            this.$message({\n              message: \"删除成功\",\n              type: \"success\"\n            });\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\"\n          });\n        });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/ticket/new_ticket.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-card class=\"box-card\" v-for=\"(item, key) in list\" :key=\"item.id\">\n      <div slot=\"header\">\n        <span class=\"card-title\">{{item.name}}</span>\n      </div>\n      <el-row :gutter=\"20\">\n        <el-col :span=\"4\" v-for=\"w in item.workflow_list\" :key=\"w.id\">\n          <div style=\"height:50px;\">\n            <router-link v-if=\"key < 4\" :class=\"'pan-btn ' + wf_color[key]\" :to=\"'/u_ticket/' + w.id\">{{w.name}}</router-link>\n            <router-link v-else :class=\"'pan-btn ' + wf_color[999]\" :to=\"'/u_ticket/' + w.id\">{{w.name}}</router-link>\n          </div>\n        </el-col>\n      </el-row>\n    </el-card>\n  </div>\n</template>\n\n<script>\nimport { workflow, auth } from \"@/api/all\";\nimport { groupBy } from \"@/utils\";\n\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate,\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"new_ticket\",\n  components: {},\n  data() {\n    return {\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false,\n      },\n      list: [],\n      wf_color: {\n        0: \"blue-btn\",\n        1: \"pink-btn\",\n        2: \"green-btn\",\n        3: \"yellow-btn\",\n        999: \"tiffany-btn\",\n      },\n      listQuery: {\n        status: true,\n      },\n    };\n  },\n  computed: {},\n  created() {\n    this.getMenuButton();\n    this.getList();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"new_ticket\")\n        .then((response) => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      workflow.requestGet(this.listQuery).then((response) => {\n        const data = response.results;\n        const map = groupBy(data, 'type')\n        for (var k of map){\n          k[0].workflow_list = k[1]\n          this.list.push(k[0])\n        }\n      });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/ticket/s_ticket.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"ticket\">\n      <el-card>\n        <div slot=\"header\" class=\"clearfix\">\n          <span class=\"card-title\">流程</span>\n        </div>\n        <el-steps :active=\"stateActive\" finish-status=\"success\" process-status=\"finish\">\n          <el-step v-for=\"item in state_list\" :key=\"item.id\" :title=\"item.name\"></el-step>\n        </el-steps>\n      </el-card>\n      <div class=\"ticket-form\" v-if=\"customfield_list.length>0\">\n        <el-form ref=\"temp\" :rules=\"rules\" :model=\"temp\" label-width=\"100px\">\n          <el-card>\n            <div slot=\"header\" class=\"card-solt\">\n              <el-form-item label=\"工单标题\">\n                <el-input disabled v-model=\"this.wfdata.name\" />\n              </el-form-item>\n            </div>\n            <el-row :gutter=\"20\">\n              <el-col\n                :md=\"{span: [7, 8, 9, 12].includes(item.customfield.field_type)? 22 : 11}\"\n                v-for=\"item in customfield_list\"\n                :key=\"item.id\"\n              >\n                <el-form-item\n                  :label=\"item.customfield.field_name\"\n                  :prop=\"item.field_key\"\n                  :rules=\"match_fields.includes(item.customfield.id)?\n                  [{ required: true, message: '请输入' + item.customfield.field_name, trigger: 'blur' },]:[]\"\n                >\n                  <el-input\n                    v-if=\"item.customfield.field_type === 1\"\n                    v-model=\"item.field_value\"\n                    :placeholder=\"item.customfield.field_name\"\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  />\n\n                  <el-input-number\n                    v-if=\"item.customfield.field_type === 2\"\n                    v-model=\"item.field_value\"\n                    :placeholder=\"item.customfield.field_name\"\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  ></el-input-number>\n\n                  <el-date-picker\n                    type=\"date\"\n                    v-if=\"item.customfield.field_type === 5\"\n                    v-model=\"item.field_value\"\n                    :placeholder=\"item.customfield.field_name\"\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  ></el-date-picker>\n\n                  <el-date-picker\n                    type=\"datetime\"\n                    v-if=\"item.customfield.field_type === 6\"\n                    v-model=\"item.field_value\"\n                    :placeholder=\"item.customfield.field_name\"\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  ></el-date-picker>\n\n                  <el-input\n                    type=\"textarea\"\n                    :autosize=\"{ minRows: 4, maxRows: 6}\"\n                    v-if=\"item.customfield.field_type === 8\"\n                    v-model=\"item.field_value\"\n                    :placeholder=\"item.customfield.field_name\"\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  ></el-input>\n\n                  <el-switch\n                    active-color=\"#13ce66\"\n                    inactive-color=\"#ff4949\"\n                    v-if=\"item.customfield.field_type === 4\"\n                    v-model=\"item.field_value\"\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  ></el-switch>\n\n                  <el-radio-group\n                    v-if=\"item.customfield.field_type === 9\"\n                    v-model=\"item.field_value\"\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  >\n                    <el-radio\n                      v-for=\"(value, index) in JSON.parse(item.customfield.field_choice)\"\n                      :key=\"index\"\n                      :label=\"index\"\n                    >{{value}}</el-radio>\n                  </el-radio-group>\n\n                  <el-checkbox-group\n                    v-if=\"item.customfield.field_type === 12\"\n                    v-model=\"item.field_value\"\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  >\n                    <el-checkbox\n                      v-for=\"(value, index)  in JSON.parse(item.customfield.field_choice)\"\n                      :key=\"index\"\n                      :label=\"index\"\n                    >{{value}}</el-checkbox>\n                  </el-checkbox-group>\n\n                  <el-select\n                    v-if=\"item.customfield.field_type === 10\"\n                    v-model=\"item.field_value\"\n                    :placeholder=\"item.customfield.field_name\"\n                    clearable\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  >\n                    <el-option\n                      v-for=\"(value, index)  in JSON.parse(item.customfield.field_choice)\"\n                      :key=\"index\"\n                      :value=\"index\"\n                    >{{value}}</el-option>\n                  </el-select>\n\n                  <el-select\n                    v-if=\"item.customfield.field_type === 13\"\n                    v-model=\"item.field_value\"\n                    :placeholder=\"item.customfield.field_name\"\n                    clearable\n                    multiple\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  >\n                    <el-option\n                      v-for=\"(value, index)  in JSON.parse(item.customfield.field_choice)\"\n                      :key=\"index\"\n                      :value=\"index\"\n                    >{{value}}</el-option>\n                  </el-select>\n\n                  <span v-if=\"item.customfield.field_type === 7\">\n                    <el-date-picker\n                      v-if=\"item.field_value.length>0\"\n                      type=\"datetimerange\"\n                      :value=\"formatDate(item.field_value)\"\n                      :placeholder=\"item.customfield.field_name\"\n                      :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                    ></el-date-picker>\n                    <el-date-picker\n                      v-else\n                      type=\"datetimerange\"\n                      :value=\"formatDate(item.field_value)\"\n                      :placeholder=\"item.customfield.field_name\"\n                      :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                    ></el-date-picker>\n                  </span>\n\n                  <el-select\n                    v-if=\"item.customfield.field_type === 11\"\n                    v-model=\"item.field_value\"\n                    :placeholder=\"item.customfield.field_name\"\n                    clearable\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  >\n                    <el-option v-for=\"t in user_list\" :key=\"t.id\" :label=\"t.username\"></el-option>\n                  </el-select>\n\n                  <el-select\n                    v-if=\"item.customfield.field_type === 14\"\n                    v-model=\"item.field_value\"\n                    :placeholder=\"item.customfield.field_name\"\n                    clearable\n                    multiple\n                    :disabled=\"deny_check || item.participant == username || item.customfield.field_attribute ||! match_fields.includes(item.customfield.id)\"\n                  >\n                    <el-option v-for=\"t in user_list\" :key=\"t.id\" :label=\"t.username\"></el-option>\n                  </el-select>\n                </el-form-item>\n              </el-col>\n            </el-row>\n            <div v-if=\"!deny_check\">\n              <el-form-item label=\"审批意见\" v-if=\"stateActive<999\">\n                <el-input\n                  type=\"textarea\"\n                  :autosize=\"{ minRows: 4, maxRows: 6}\"\n                  v-model=\"wfdata.memo\"\n                  placeholder=\"说些啥吧\"\n                ></el-input>\n              </el-form-item>\n\n              <el-form-item>\n                <span style=\"margin: 0 5px;\" v-for=\"item in transition_list\" :key=\"item.id\">\n                  <el-button\n                    v-if=\"item.name===1\"\n                    :type=\"btn_types[item.name]\"\n                    @click=\"selectUser('temp', item)\"\n                  >{{item.name|TransitionNameFilter}}</el-button>\n                  <el-button\n                    v-else\n                    :type=\"btn_types[item.name]\"\n                    @click=\"handleButton('temp', item)\"\n                  >{{item.name|TransitionNameFilter}}</el-button>\n                </span>\n              </el-form-item>\n            </div>\n          </el-card>\n        </el-form>\n      </div>\n\n      <el-card>\n        <div slot=\"header\" class=\"clearfix\">\n          <span class=\"card-title\">审批历史</span>\n        </div>\n        <el-timeline>\n          <el-timeline-item\n            v-for=\"item in ticketlog_list\"\n            :key=\"item.id\"\n            :timestamp=\"item.create_time\"\n            icon=\"el-icon-s-help\"\n            size=\"large\"\n            color=\"#0bbd87\"\n            placement=\"top\"\n          >\n            <el-card class=\"check_history\">\n              <div class=\"check_history_title\">\n                时间：<a class=\"state\">{{item.create_time}}</a>\n                |\n                节点：<a class=\"transition-status\">{{item.state.name}}</a>\n                |\n                操作：<a\n                  class=\"transition-name\"\n                >{{item.transition.name|TransitionNameFilter}}</a>\n              </div>\n              <el-form label-position=\"left\">\n                <el-form-item label=\"处理人\">\n                  <span>{{ item.participant }}</span>\n                </el-form-item>\n                <el-form-item label=\"处理意见\">\n                  <span>{{ item.suggestion }}</span>\n                </el-form-item>\n              </el-form>\n            </el-card>\n          </el-timeline-item>\n        </el-timeline>\n      </el-card>\n    </div>\n\n    <el-dialog :visible.sync=\"dialogVisible\">\n      <div slot=\"title\">\n        当前下一步处理对象是：\n        <a style=\"color: red; font-size: 24px\">{{\n          participant_type[dialogChooiceType]\n        }}</a>\n        <span v-if=\"dialogChooiceType==='none'\">请直接点确定</span>\n        <span v-else>请点击下方处理对象，选择转交用户</span>\n      </div>\n      <el-row :gutter=\"20\">\n        <el-col :span=\"8\">\n          <el-collapse v-model=\"dialogChooiceType\" accordion @change=\"selectType\">\n            <el-collapse-item title=\"用户\" name=\"user\" disabled></el-collapse-item>\n            <el-collapse-item title=\"部门\" name=\"group\" disabled>\n              <div v-if=\"group_role_list.length > 0\">\n                <li v-for=\"item in group_role_list\" :key=\"item.id\">\n                  <el-button\n                    style=\"margin: 2px\"\n                    size=\"mini\"\n                    plain\n                    @click=\"checkGroupUser(item.id)\"\n                  >\n                    {{ item.name }}\n                  </el-button>\n                </li>\n              </div>\n              <div v-else>\n                没有可选{{ participant_type[dialogChooiceType] }}\n              </div>\n            </el-collapse-item>\n            <el-collapse-item title=\"角色\" name=\"role\" disabled>\n              <div v-if=\"group_role_list.length > 0\">\n                <li v-for=\"item in group_role_list\" :key=\"item.id\">\n                  <el-button\n                    style=\"margin: 2px\"\n                    size=\"mini\"\n                    plain\n                    @click=\"checkRoleUser(item.id)\"\n                  >\n                    {{ item.name }}\n                  </el-button>\n                </li>\n              </div>\n              <div v-else>\n                没有可选{{ participant_type[dialogChooiceType] }}\n              </div>\n            </el-collapse-item>\n          </el-collapse>\n        </el-col>\n        <el-col :span=\"16\">\n          <el-table :data=\"choice_user_list\" @row-click=\"checkTableUser\" style=\"width: 100%\">\n            <el-table-column prop=\"username\" label=\"用户\" width=\"180\"></el-table-column>\n            <el-table-column prop=\"realname\" label=\"姓名\"></el-table-column>\n          </el-table>\n        </el-col>\n      </el-row>\n\n      <el-row>\n        <el-input readonly v-model=\"wfdata.participant\">\n          <template slot=\"prepend\">选择用户</template>\n        </el-input>\n      </el-row>\n\n      <span slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogVisible = false\">取 消</el-button>\n        <el-button\n          :disabled=\"wfdata.participant?false:true\"\n          type=\"primary\"\n          @click=\"handleButton('temp', choice_transition)\"\n        >确 定</el-button>\n      </span>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport {\n  ticketcustomfield,\n  state,\n  transition,\n  ticket,\n  ticketflowlog,\n  user,\n  auth,\n} from \"@/api/all\";\nimport { mapGetters } from \"vuex\";\n\nexport default {\n  name: \"s_ticket\",\n\n  components: {},\n  data() {\n    return {\n      visible: false,\n      tempRoute: {},\n      wfdata: {},\n      customfield_list: [],\n      state_list: [],\n      transition_list: [],\n      ticketlog_list: [],\n      user_list: [],\n      temp: {},\n      rules: {},\n      btn_types: {\n        0: \"primary\",\n        1: \"success\",\n        2: \"warning\",\n        3: \"danger\",\n        4: \"danger\",\n      },\n      match_fields: [],\n      workflow_temp: {\n        participant: this.username,\n        is_hidden: false,\n      },\n      stateActive: 999,\n      choice_user_list: [],\n      dialogVisible: false,\n      dialogChooiceType: \"none\",\n      participant_list: [],\n      choice_transition: {},\n      group_role_list: [],\n      participant_type: {\n        \"none\": \"无处理人\",\n        \"user\": \"个人\",\n        \"group\": \"部门\",\n        \"role\": \"角色\",\n      },\n      deny_check: false, // 允许审核\n    };\n  },\n  computed: {\n    ...mapGetters([\"username\"]),\n    formatDate() {\n      return function (date) {\n        const d = eval(\"(\" + date + \")\");\n        return d;\n      };\n    },\n  },\n  created() {\n    const id = this.$route.params && this.$route.params.id;\n    this.fetchData(id);\n    this.getUserList();\n    this.tempRoute = Object.assign({}, this.$route);\n  },\n  methods: {\n    fetchData(id) {\n      this.workflow_temp.ticket = id;\n      const params = {\n        id: id,\n      };\n      ticket.requestGet(params).then((response) => {\n        this.wfdata = response.results[0];\n        if (this.wfdata.participant !== this.username) {\n          this.deny_check = true;\n        }\n        this.wfdata.memo = \"\";\n        this.setPageTitle();\n\n        this.workflow_temp.workflow = this.wfdata.workflow.id;\n        this.workflow_temp.source_state = this.wfdata.state.id;\n        this.match_fields = this.wfdata.state.fields;\n\n        this.getCustomfieldList();\n        this.getStateList();\n        this.getTicketlogList();\n      });\n    },\n    getCustomfieldList() {\n      ticketcustomfield.requestGet(this.workflow_temp).then((response) => {\n        this.customfield_list = response.results;\n      });\n    },\n    getStateList() {\n      state.requestGet(this.workflow_temp).then((response) => {\n        this.state_list = response.results;\n        for (var i in this.state_list) {\n          if (\n            this.state_list[i].id == this.wfdata.state.id &&\n            this.wfdata.state.state_type < 2\n          ) {\n            this.stateActive = parseInt(i);\n            break;\n          }\n        }\n        this.getTransitionList();\n      });\n    },\n    getTransitionList() {\n      transition.requestGet(this.workflow_temp).then((response) => {\n        this.transition_list = response.results;\n      });\n    },\n    getTicketlogList() {\n      ticketflowlog.requestGet(this.workflow_temp).then((response) => {\n        this.ticketlog_list = response.results;\n      });\n    },\n    getUserList() {\n      user.requestGet().then((response) => {\n        this.user_list = response.results;\n      });\n    },\n    handleFilter() {\n      this.fetchData();\n    },\n    setPageTitle() {\n      const title = this.wfdata.name;\n      document.title = `${title} - 审批`;\n    },\n    selectUser(dataForm, row) {\n      this.$refs[dataForm].validate((valid) => {\n        if (valid) {\n          this.dialogVisible = true;\n          this.choice_transition = row;\n          this.dialogChooiceType = row.dest_state.participant_type;\n          this.selectType(this.dialogChooiceType);\n        }\n      });\n    },\n    selectType(val) {\n      if (val === 'user') {\n        this.choice_user_list = this.choice_transition.dest_state.user_participant;\n      } else if (val == 'group') {\n        this.group_role_list = this.choice_transition.dest_state.group_participant;\n      } else if (val == 'role') {\n        this.group_role_list = this.choice_transition.dest_state.role_participant;\n      } else {\n        this.group_role_list = [];\n      }\n    },\n    checkGroupUser(id) {\n      const params = {\n        group: id,\n      };\n      user.requestGet(params).then((response) => {\n        this.choice_user_list = response.results;\n      });\n    },\n    checkRoleUser(id) {\n      const params = {\n        roles: id,\n      };\n      user.requestGet(params).then((response) => {\n        this.choice_user_list = response.results;\n      });\n    },\n    checkTableUser(row) {\n      this.wfdata.participant = row.username;\n    },\n    handleButton(dataForm, transition) {\n      const customfield = [];\n      for (var i of this.customfield_list) {\n        customfield.push({\n          id: i.id,\n          ticket: i.ticket.id,\n          customfield: i.customfield.id,\n          field_value: i.field_value,\n        });\n      }\n      const data = Object.assign({}, this.wfdata, {\n        create_user: this.wfdata.create_user.id,\n        workflow: this.wfdata.workflow.id,\n        participant: this.wfdata.participant,\n        state: transition.dest_state.id,\n        transition: transition.id,\n        customfield: JSON.stringify(customfield),\n        relation: this.username,\n      });\n      if (transition.name == 1) {\n        data.participant = this.wfdata.participant;\n        data.relation = this.wfdata.participant;\n      } else {\n        data.participant = this.ticketlog_list[this.ticketlog_list.length-1].participant;\n      }\n      this.$refs[dataForm].validate((valid) => {\n        if (valid) {\n          ticket\n            .requestPut(this.wfdata.id, data)\n            .then((response) => {\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000,\n              });\n              this.$router.push({ path: \"/todo_ticket\" });\n            })\n            .catch(() => {});\n        }\n      });\n    },\n  },\n};\n</script>\n\n<style scoped lang=\"scss\">\n.check_history {\n  .check_history_title {\n    font-weight: 700;\n    margin: 10px 0;\n    .state {\n      color: #0bbd87;\n    }\n    .transition-status {\n      color: #f1bd5f;\n    }\n    .transition-name {\n      color: #f32db1;\n    }\n  }\n  .el-form-item {\n    margin-bottom: 5px;\n  }\n}\n</style>"
  },
  {
    "path": "frontend/src/views/ticket/todo_ticket.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      style=\"width: 100%\"\n      highlight-current-row\n      @sort-change=\"handleSortChange\"\n    >\n      <el-table-column label=\"名称\" prop=\"name\"></el-table-column>\n      <el-table-column label=\"工单流水号\" prop=\"sn\" width=\"240\">\n        <template slot-scope=\"{ row }\">\n          <router-link :to=\"'/s_ticket/' + row.id\">\n            <el-link type=\"success\">{{ row.sn }}</el-link>\n          </router-link>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"申请人\" prop=\"create_user\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.create_user.username }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"当前环节\" prop=\"state\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.state.name }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"当前状态\" prop=\"transition\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.transition.attribute_type | AttributeTypeFilter }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"申请人\" prop=\"create_user\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.create_user.username }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" prop=\"create_time\"></el-table-column>\n    </el-table>\n  </div>\n</template>\n\n<script>\nimport { ticket, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate,\n} from \"@/utils/permission\";\nimport { mapGetters } from \"vuex\";\n\nexport default {\n  name: \"todo_ticket\",\n\n  components: { Pagination },\n  data() {\n    return {\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false,\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      listQuery: {\n        // offset: 1,\n        // limit: 20,\n        search: undefined,\n        ordering: undefined,\n        participant: this.username,\n        transition__attribute_type__lt: 4,\n      },\n    };\n  },\n  computed: {\n    ...mapGetters([\"username\"]),\n  },\n  created() {\n    this.getMenuButton();\n    this.getList();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"todo_ticket\")\n        .then((response) => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      this.listQuery.participant = this.username;\n      ticket.requestGet(this.listQuery).then((response) => {\n        this.list = response.results;\n        this.listLoading = false;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    },\n    handleDelete(row) {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\",\n      })\n        .then(() => {\n          ticket.requestDelete(row.id).then(() => {\n            this.$message({\n              message: \"删除成功\",\n              type: \"success\",\n            });\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\",\n          });\n        });\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/ticket/u_ticket.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"ticket\">\n      <div class=\"ticket-form\" v-if=\"customfield_list.length > 0\">\n        <el-form ref=\"temp\" :model=\"temp\" label-width=\"100px\">\n          <el-card>\n            <div slot=\"header\" class=\"card-solt\">\n              <el-form-item label=\"工单标题\">\n                <el-input v-model=\"ticket.name\" />\n              </el-form-item>\n            </div>\n            <el-row :gutter=\"20\">\n              <el-col\n                :md=\"{\n                  span: [7, 8, 9, 12].includes(item.field_type) ? 22 : 11,\n                }\"\n                v-for=\"item in customfield_list\"\n                :key=\"item.id\"\n              >\n                <el-form-item\n                  v-show=\"!item.field_attribute\"\n                  :label=\"item.field_name\"\n                  :prop=\"item.field_key\"\n                  :rules=\"\n                    match_fields.includes(item.id)\n                      ? [\n                          {\n                            required: true,\n                            message: '请输入' + item.field_name,\n                            trigger: 'blur',\n                          },\n                        ]\n                      : []\n                  \"\n                >\n                  <el-input\n                    v-if=\"item.field_type === 1\"\n                    v-model=\"temp[item.field_key]\"\n                    :placeholder=\"item.field_name\"\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  />\n\n                  <el-input-number\n                    v-if=\"item.field_type === 2\"\n                    v-model=\"temp[item.field_key]\"\n                    :placeholder=\"item.field_name\"\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  ></el-input-number>\n\n                  <el-date-picker\n                    type=\"date\"\n                    v-if=\"item.field_type === 5\"\n                    v-model=\"temp[item.field_key]\"\n                    :placeholder=\"item.field_name\"\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  ></el-date-picker>\n\n                  <el-date-picker\n                    type=\"datetime\"\n                    v-if=\"item.field_type === 6\"\n                    v-model=\"temp[item.field_key]\"\n                    :placeholder=\"item.field_name\"\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  ></el-date-picker>\n\n                  <el-input\n                    type=\"textarea\"\n                    :autosize=\"{ minRows: 4, maxRows: 6 }\"\n                    v-if=\"item.field_type === 8\"\n                    v-model=\"temp[item.field_key]\"\n                    :placeholder=\"item.field_name\"\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  ></el-input>\n\n                  <el-switch\n                    active-color=\"#13ce66\"\n                    inactive-color=\"#ff4949\"\n                    v-if=\"item.field_type === 4\"\n                    v-model=\"temp[item.field_key]\"\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  ></el-switch>\n\n                  <el-radio-group\n                    v-if=\"item.field_type === 9\"\n                    v-model=\"temp[item.field_key]\"\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  >\n                    <el-radio\n                      v-for=\"(value, index) in JSON.parse(item.field_choice)\"\n                      :key=\"index\"\n                      :label=\"index\"\n                      >{{ value }}</el-radio\n                    >\n                  </el-radio-group>\n\n                  <el-checkbox-group\n                    v-if=\"item.field_type === 12\"\n                    v-model=\"temp[item.field_key]\"\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  >\n                    <el-checkbox\n                      v-for=\"(value, index) in JSON.parse(item.field_choice)\"\n                      :key=\"index\"\n                      :label=\"index\"\n                      >{{ value }}</el-checkbox\n                    >\n                  </el-checkbox-group>\n\n                  <el-select\n                    v-if=\"item.field_type === 10\"\n                    v-model=\"temp[item.field_key]\"\n                    :placeholder=\"item.field_name\"\n                    clearable\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  >\n                    <el-option\n                      v-for=\"(value, index) in JSON.parse(item.field_choice)\"\n                      :key=\"index\"\n                      :value=\"index\"\n                      >{{ value }}</el-option\n                    >\n                  </el-select>\n\n                  <el-select\n                    v-if=\"item.field_type === 13\"\n                    v-model=\"temp[item.field_key]\"\n                    :placeholder=\"item.field_name\"\n                    clearable\n                    multiple\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  >\n                    <el-option\n                      v-for=\"(value, index) in JSON.parse(item.field_choice)\"\n                      :key=\"index\"\n                      :value=\"index\"\n                      >{{ value }}</el-option\n                    >\n                  </el-select>\n\n                  <el-date-picker\n                    v-if=\"item.field_type === 7\"\n                    v-model=\"temp[item.field_key]\"\n                    format=\"yyyy 年 MM 月 dd 日\"\n                    value-format=\"yyyy-MM-dd\"\n                    type=\"daterange\"\n                    range-separator=\"至\"\n                    start-placeholder=\"开始日期\"\n                    end-placeholder=\"结束日期\"\n                  ></el-date-picker>\n\n                  <el-select\n                    v-if=\"item.field_type === 11\"\n                    v-model=\"temp[item.field_key]\"\n                    :placeholder=\"item.field_name\"\n                    clearable\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  >\n                    <el-option\n                      v-for=\"t in user_list\"\n                      :key=\"t.id\"\n                      :label=\"t.username\"\n                      :value=\"t.username\"\n                    ></el-option>\n                  </el-select>\n\n                  <el-select\n                    v-if=\"item.field_type === 14\"\n                    v-model=\"temp[item.field_key]\"\n                    :placeholder=\"item.field_name\"\n                    clearable\n                    multiple\n                    :disabled=\"\n                      item.field_attribute || !match_fields.includes(item.id)\n                    \"\n                  >\n                    <el-option\n                      v-for=\"t in user_list\"\n                      :key=\"t.id\"\n                      :label=\"t.username\"\n                      :value=\"t.username\"\n                    ></el-option>\n                  </el-select>\n                </el-form-item>\n              </el-col>\n            </el-row>\n            <el-form-item>\n              <span\n                style=\"margin: 0 5px\"\n                v-for=\"item in transition_list\"\n                :key=\"item.id\"\n              >\n                <el-button\n                  v-if=\"item.name === 1\"\n                  :type=\"btn_types[item.name]\"\n                  @click=\"selectUser('temp', item)\"\n                  >{{ item.name | TransitionNameFilter }}</el-button\n                >\n                <el-button\n                  v-else\n                  :type=\"btn_types[item.name]\"\n                  @click=\"handleSave('temp', item)\"\n                  >{{ item.name | TransitionNameFilter }}</el-button\n                >\n              </span>\n            </el-form-item>\n          </el-card>\n        </el-form>\n      </div>\n      <div v-else>\n        <h1 style=\"text-align: center\">没有设置工作流字段</h1>\n      </div>\n    </div>\n\n    <el-dialog :visible.sync=\"dialogVisible\">\n      <div slot=\"title\">\n        当前下一步处理对象是：\n        <a style=\"color: red; font-size: 24px\">{{\n          participant_type[dialogChooiceType]\n        }}</a>\n        <span v-if=\"dialogChooiceType==='none'\">请直接点确定</span>\n        <span v-else>请点击下方处理对象，选择转交用户</span>\n      </div>\n      <el-row :gutter=\"20\">\n        <el-col :span=\"8\">\n          <el-collapse\n            v-model=\"dialogChooiceType\"\n            accordion\n            @change=\"selectType\"\n          >\n            <el-collapse-item title=\"用户\" name=\"user\" disabled></el-collapse-item>\n            <el-collapse-item title=\"部门\" name=\"group\" disabled>\n              <div v-if=\"group_role_list.length > 0\">\n                <li v-for=\"item in group_role_list\" :key=\"item.id\">\n                  <el-button\n                    style=\"margin: 2px\"\n                    size=\"mini\"\n                    plain\n                    @click=\"checkGroupUser(item.id)\"\n                  >\n                    {{ item.name }}\n                  </el-button>\n                </li>\n              </div>\n              <div v-else>\n                没有可选{{ participant_type[dialogChooiceType] }}\n              </div>\n            </el-collapse-item>\n            <el-collapse-item title=\"角色\" name=\"role\" disabled>\n              <div v-if=\"group_role_list.length > 0\">\n                <li v-for=\"item in group_role_list\" :key=\"item.id\">\n                  <el-button\n                    style=\"margin: 2px\"\n                    size=\"mini\"\n                    plain\n                    @click=\"checkRoleUser(item.id)\"\n                  >\n                    {{ item.name }}\n                  </el-button>\n                </li>\n              </div>\n              <div v-else>\n                没有可选{{ participant_type[dialogChooiceType] }}\n              </div>\n            </el-collapse-item>\n          </el-collapse>\n        </el-col>\n        <el-col :span=\"16\">\n          <el-table\n            :data=\"choice_user_list\"\n            @row-click=\"checkTableUser\"\n            style=\"width: 100%\"\n          >\n            <el-table-column\n              prop=\"username\"\n              label=\"用户\"\n              width=\"180\"\n            ></el-table-column>\n            <el-table-column prop=\"realname\" label=\"姓名\"></el-table-column>\n          </el-table>\n        </el-col>\n      </el-row>\n\n      <el-row>\n        <el-input readonly v-model=\"ticket.participant\">\n          <template slot=\"prepend\">选择用户</template>\n        </el-input>\n      </el-row>\n\n      <span slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogVisible = false\">取 消</el-button>\n        <el-button\n          :disabled=\"ticket.participant ? false : true\"\n          type=\"primary\"\n          @click=\"handleButton('temp', choice_transition)\"\n          >确 定</el-button\n        >\n      </span>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport {\n  workflow,\n  customfield,\n  state,\n  transition,\n  ticket,\n  user,\n  auth,\n} from \"@/api/all\";\n\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate,\n} from \"@/utils/permission\";\nimport { mapGetters } from \"vuex\";\nimport { GenDatetime } from \"@/utils\";\n\nexport default {\n  name: \"u_ticket\",\n\n  components: {},\n  data() {\n    return {\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false,\n      },\n      tempRoute: {},\n      wfdata: {},\n      customfield_list: [],\n      state_list: [],\n      transition_list: [],\n      user_list: [],\n      temp: {},\n      rules: {},\n      btn_types: {\n        0: \"primary\",\n        1: \"success\",\n        2: \"warning\",\n        3: \"danger\",\n      },\n      match_fields: [],\n      ticket: {\n        name: \"\",\n        participant: \"\",\n      },\n      workflow_temp: {\n        participant: this.user_id,\n      },\n      choice_user_list: [],\n      dialogChooiceType: \"none\",\n      dialogVisible: false,\n      participant_list: [],\n      choice_transition: {},\n      group_role_list: [],\n      participant_type: {\n        \"none\": \"无处理人\",\n        \"user\": \"个人\",\n        \"group\": \"部门\",\n        \"role\": \"角色\",\n      },\n    };\n  },\n  computed: {\n    ...mapGetters([\"username\", \"user_id\"]),\n  },\n  created() {\n    const id = this.$route.params && this.$route.params.id;\n    this.fetchData(id);\n    this.getUserList();\n    this.tempRoute = Object.assign({}, this.$route);\n  },\n  methods: {\n    fetchData(id) {\n      this.workflow_temp.workflow = id;\n      const params = {\n        id: id,\n      };\n      workflow.requestGet(params).then((response) => {\n        this.wfdata = response.results[0];\n        this.setPageTitle();\n\n        const d = new Date();\n        this.ticket.name = this.wfdata.name + \"-\" + GenDatetime(d);\n        this.getCustomfieldList();\n        this.getStateList();\n      });\n    },\n    getCustomfieldList() {\n      customfield.requestGet(this.workflow_temp).then((response) => {\n        this.customfield_list = response.results;\n      });\n    },\n    getStateList(id) {\n      state.requestGet(this.workflow_temp).then((response) => {\n        this.state_list = response.results;\n        this.match_fields = this.state_list[1].fields;\n        this.getTransitionList(this.state_list[0].id);\n      });\n    },\n    getTransitionList(source_state) {\n      this.workflow_temp.source_state = source_state;\n      transition.requestGet(this.workflow_temp).then((response) => {\n        this.transition_list = response.results;\n      });\n    },\n    getUserList() {\n      user.requestGet().then((response) => {\n        this.user_list = response.results;\n      });\n    },\n    handleFilter() {\n      this.fetchData();\n    },\n    setPageTitle() {\n      const title = this.wfdata.name;\n      document.title = `${title} - 创建`;\n    },\n    selectUser(dataForm, row) {\n      this.$refs[dataForm].validate((valid) => {\n        if (valid) {\n          this.dialogVisible = true;\n          this.choice_transition = row;\n          this.dialogChooiceType = row.dest_state.participant_type;\n          this.selectType(this.dialogChooiceType);\n        }\n      });\n    },\n    selectType(val) {\n      if (val === 'user') {\n        this.choice_user_list = this.choice_transition.dest_state.user_participant;\n      } else if (val == 'group') {\n        this.group_role_list = this.choice_transition.dest_state.group_participant;\n      } else if (val == 'role') {\n        this.group_role_list = this.choice_transition.dest_state.role_participant;\n      } else {\n        this.ticket.participant = this.username\n      }\n    },\n    checkGroupUser(id) {\n      const params = {\n        group: id,\n      };\n      user.requestGet(params).then((response) => {\n        this.choice_user_list = response.results;\n      });\n    },\n    checkRoleUser(id) {\n      const params = {\n        roles: id,\n      };\n      user.requestGet(params).then((response) => {\n        this.choice_user_list = response.results;\n      });\n    },\n    checkTableUser(row) {\n      this.ticket.participant = row.username;\n    },\n    handleSave(dataForm, transition) {\n      this.ticket.participant = this.username;\n      this.handleButton(dataForm, transition);\n    },\n    handleButton(dataForm, transition) {\n      this.dialogVisible = false;\n      const customfield = [];\n      for (var i of this.customfield_list) {\n        if (this.temp[i.field_key]) {\n          customfield.push({\n            customfield: i.id,\n            field_key: i.field_key,\n            field_value: this.temp[i.field_key],\n          });\n        } else {\n          customfield.push({\n            customfield: i.id,\n            field_key: i.field_key,\n            field_value: \"\",\n          });\n        }\n      }\n      const data = Object.assign(\n        {},\n        {\n          name: this.ticket.name,\n          participant: this.ticket.participant,\n          create_user: this.user_id,\n          workflow: this.wfdata.id,\n          state: transition.dest_state.id,\n          transition: transition.id,\n          customfield: JSON.stringify(customfield),\n          relation: this.ticket.participant,\n        }\n      );\n          ticket\n            .requestPost(data)\n            .then((res) => {\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000,\n              });\n              this.$router.push({ path: \"/my_ticket\" });\n            })\n            .catch(e => {\n              console.log(e)\n            });\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/tool/audit.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-input\n        v-model=\"listQuery.search\"\n        placeholder=\"请输入内容\"\n        clearable\n        prefix-icon=\"el-icon-search\"\n        style=\"width: 200px;\"\n        class=\"filter-item\"\n        @keyup.enter.native=\"handleFilter\"\n        @clear=\"handleFilter\"\n      />\n      <el-select class=\"filter-item\" clearable v-model=\"listQuery.method\" placeholder=\"请选择请求方式\">\n        <el-option\n          v-for=\"item in ['GET', 'POST','PUT','DELETE']\"\n          :key=\"item.id\"\n          :label=\"item\"\n          :value=\"item\"\n        ></el-option>\n      </el-select>\n      <el-button\n        class=\"filter-item\"\n        type=\"primary\"\n        icon=\"el-icon-search\"\n        @click=\"handleFilter\"\n      >{{ \"搜索\" }}</el-button>\n    </div>\n\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      border\n      style=\"width: 100%\"\n      @sort-change=\"handleSortChange\"\n    >\n      <el-table-column label=\"请求方法\" prop=\"method\" width=\"100\"></el-table-column>\n      <el-table-column label=\"请求路径\" prop=\"url\"></el-table-column>\n      <el-table-column label=\"请求参数\" prop=\"query_string\">\n        <template slot-scope=\"{row}\">\n          <el-tag size=\"mini\">query_params</el-tag>\n          : {{JSON.parse(row.query_string).query_params}}\n          <br />\n          <el-tag size=\"mini\">json</el-tag>\n          : {{JSON.parse(row.query_string).json}}\n        </template>\n      </el-table-column>\n      <el-table-column label=\"请求用户\" prop=\"user\" width=\"100\"></el-table-column>\n      <el-table-column label=\"请求ip\" prop=\"remote_ip\" width=\"100\"></el-table-column>\n      <el-table-column label=\"请求时间\" prop=\"create_time\" width=\"200\"></el-table-column>\n    </el-table>\n    <div class=\"table-pagination\">\n      <pagination\n        v-show=\"total > 0\"\n        :total=\"total\"\n        :page.sync=\"listQuery.offset\"\n        :limit.sync=\"listQuery.limit\"\n        @pagination=\"getList\"\n      />\n    </div>\n  </div>\n</template>\n\n<script>\nimport { audit, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"audit\",\n\n  components: { Pagination },\n  data() {\n    return {\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      loading: true,\n      listQuery: {\n        offset: 1,\n        limit: 20,\n        search: undefined,\n        ordering: undefined\n      }\n    };\n  },\n  computed: {},\n  created() {\n    this.getMenuButton();\n    this.getList();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"audit\")\n        .then(response => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      audit.requestGet(this.listQuery).then(response => {\n        this.list = response.results;\n        this.total = response.count;\n        this.listLoading = false;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/tool/test.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <h1>这是一个测试页面</h1>\n    <h3>下面是你对这个页面的相关操作权限，如果没有，则不会显示相应的按钮</h3>\n    <el-button v-if=\"permissionList.add\" size=\"small\" type=\"primary\">\n      {{ \"增加\" }}\n    </el-button>\n\n    <el-button v-if=\"permissionList.del\" size=\"small\" type=\"danger\">\n      {{ \"删除\" }}\n    </el-button>\n\n    <el-button v-if=\"permissionList.update\" size=\"small\" type=\"warning\">\n      {{ \"编辑\" }}\n    </el-button>\n\n    <el-button v-if=\"permissionList.view\" size=\"small\" type=\"success\">\n      {{ \"查看\" }}\n    </el-button>\n  </div>\n</template>\n\n<script>\n  import {auth} from '@/api/all'\n  import {checkAuthAdd, checkAuthDel, checkAuthView, checkAuthUpdate} from '@/utils/permission'\n\n  export default {\n    name: 'test',\n    data() {\n      return {\n        operationList: [],\n        permissionList: {\n          add: false,\n          del: false,\n          view: false,\n          update: false\n        }\n      }\n    },\n    created() {\n      this.getMenuButton()\n    },\n    methods: {\n      checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n      },\n      getMenuButton() {\n        auth.requestMenuButton('test').then(response => {\n          this.operationList = response.results\n        }).then(() => {\n          this.checkPermission()\n        })\n      },\n    }\n  }\n</script>\n"
  },
  {
    "path": "frontend/src/views/workflow/pages/customfield.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-button-group>\n        <el-button\n          v-if=\"permissionList.add\"\n          class=\"filter-item\"\n          type=\"success\"\n          icon=\"el-icon-edit\"\n          @click=\"handleCreate\"\n        >{{ \"添加\" }}</el-button>\n      </el-button-group>\n    </div>\n\n    <el-table :data=\"list\" border style=\"width: 100%\" highlight-current-row>\n      <el-table-column label=\"名称\" prop=\"field_name\"></el-table-column>\n      <el-table-column label=\"标识\" prop=\"field_key\"></el-table-column>\n      <el-table-column label=\"类型\" prop=\"field_type\">\n        <template slot-scope=\"{ row }\">\n          <span>{{row.field_type|FieldTypeFilter}}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"字段是否内置\" prop=\"field_attribute\">\n        <template slot-scope=\"{ row }\">\n          <el-tag v-if=\"row.field_attribute\" type=\"success\">是</el-tag>\n          <el-tag v-else type=\"danger\">否</el-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"排序\" prop=\"order_id\"></el-table-column>\n      <el-table-column label=\"默认值\" prop=\"default_value\"></el-table-column>\n      <el-table-column label=\"标签\" prop=\"label\"></el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" width=\"260\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"{ row }\">\n          <el-button-group v-if=\"!row.field_attribute\">\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"small\"\n              type=\"primary\"\n              @click=\"handleUpdate(row)\"\n            >{{ \"编辑\" }}</el-button>\n            <el-button\n              v-if=\"permissionList.del\"\n              size=\"small\"\n              type=\"danger\"\n              @click=\"handleDelete(row)\"\n            >{{ \"删除\" }}</el-button>\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <el-dialog\n      :title=\"textMap[dialogStatus]\"\n      :visible.sync=\"dialogFormVisible\"\n      :close-on-click-modal=\"false\"\n    >\n      <el-form\n        ref=\"dataForm\"\n        :rules=\"rules\"\n        :model=\"temp\"\n        label-position=\"left\"\n        label-width=\"100px\"\n        style=\"width: 400px; margin-left:50px;\"\n      >\n        <el-form-item label=\"字段名称\" prop=\"field_name\">\n          <el-input v-model=\"temp.field_name\" />\n        </el-form-item>\n        <el-form-item label=\"字段标识\" prop=\"field_key\">\n          <el-input v-model=\"temp.field_key\" />\n          <a class=\"tips\">字段类型请尽量特殊，避免与系统中关键字冲突</a>\n        </el-form-item>\n        <el-form-item label=\"字段类型\" prop=\"field_type\">\n          <el-select v-model=\"temp.field_type\" clearable placeholder=\"请选择\">\n            <el-option\n              v-for=\"(label, value) in field_types\"\n              :key=\"value\"\n              :label=\"label\"\n              :value=\"parseInt(value)\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"排序\" prop=\"order_id\">\n          <el-input v-model=\"temp.order_id\" />\n        </el-form-item>\n        <el-form-item label=\"默认值\" prop=\"default_value\">\n          <el-input v-model=\"temp.default_value\" />\n          <a class=\"tips\">前端展示时，可以将此内容作为表单中的该字段的默认值</a>\n        </el-form-item>\n        <el-form-item label=\"文本域模板\" prop=\"field_template\" v-if=\"temp.field_type===9\">\n          <el-input v-model=\"temp.field_template\" />\n          <a class=\"tips\">文本域类型字段前端显示时可以将此内容作为字段的placeholder</a>\n        </el-form-item>\n        <el-form-item label=\"布尔类型显示名\" prop=\"boolean_field_display\" v-if=\"temp.field_type===4\">\n          <el-input v-model=\"temp.boolean_field_display\" />\n          <a class=\"tips\">当为布尔类型时候，可以支持自定义显示形式。{\"1\":\"是\",\"0\":\"否\"}或{\"1\":\"需要\",\"0\":\"不需要\"}，注意数字也需要引号</a>\n        </el-form-item>\n        <el-form-item label=\"多选值\" prop=\"field_choice\" v-if=\"[9,10,12,13].includes(temp.field_type)\">\n          <el-input v-model=\"temp.field_choice\" />\n          <a class=\"tips\">radio,select:{\"1\":\"中国\", \"2\":\"美国\"},注意数字也需要引号</a>\n        </el-form-item>\n        <el-form-item label=\"标签\" prop=\"label\">\n          <el-input v-model=\"temp.label\" />\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"memo\">\n          <el-input v-model=\"temp.memo\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogFormVisible = false\">{{ \"取消\" }}</el-button>\n        <el-button\n          type=\"primary\"\n          @click=\"dialogStatus === 'create' ? createData() : updateData()\"\n        >{{ \"确定\" }}</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { customfield, auth } from \"@/api/all\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"customfield\",\n  props: {\n    wfdata: {\n      type: Object,\n      default: {}\n    },\n    list: {\n      type: Array,\n      default: []\n    }\n  },\n\n  components: {},\n  data() {\n    return {\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false\n      },\n      temp: {},\n      dialogFormVisible: false,\n      dialogStatus: \"\",\n      textMap: {\n        update: \"编辑\",\n        create: \"添加\"\n      },\n      rules: {\n        name: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }]\n      },\n      field_types: {\n        1: \"字符串\",\n        2: \"整形\",\n        3: \"浮点型\",\n        4: \"布尔\",\n        5: \"日期\",\n        6: \"日期时间\",\n        7: \"范围日期\",\n        8: \"文本域\",\n        9: \"单选框\",\n        10: \"下拉列表\",\n        11: \"用户名\",\n        12: \"多选框\",\n        13: \"多选下拉\",\n        14: \"多选用户名\"\n      }\n    };\n  },\n  computed: {},\n  created() {\n    this.getMenuButton();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"customfield\")\n        .then(response => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    handleFilter() {},\n    resetTemp() {\n      this.temp = {\n        memo: \"\",\n        field_type: 1,\n        field_key: \"\",\n        field_name: \"\",\n        order_id: 10,\n        default_value: \"\",\n        field_template: \"\",\n        boolean_field_display: \"\",\n        field_choice: \"\",\n        label: \"\",\n        workflow: this.wfdata.id\n      };\n    },\n    handleCreate() {\n      this.resetTemp();\n      this.dialogStatus = \"create\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    createData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          customfield\n            .requestPost(this.temp)\n            .then(response => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000\n              });\n              this.$emit(\"checkdata\");\n            })\n            .catch(() => {});\n        }\n      });\n    },\n    handleUpdate(row) {\n      this.temp = row;\n      this.dialogStatus = \"update\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    updateData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          customfield\n            .requestPut(this.temp.id, this.temp)\n            .then(() => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000\n              });\n              this.$emit(\"checkdata\");\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleDelete(row) {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\"\n      })\n        .then(() => {\n          customfield.requestDelete(row.id).then(() => {\n            this.$message({\n              message: \"删除成功\",\n              type: \"success\"\n            });\n            this.$emit(\"checkdata\");\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\"\n          });\n        });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/workflow/pages/state.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-button-group>\n        <el-button\n          v-if=\"permissionList.add\"\n          class=\"filter-item\"\n          type=\"success\"\n          icon=\"el-icon-edit\"\n          @click=\"handleCreate\"\n        >{{ \"添加\" }}</el-button>\n      </el-button-group>\n    </div>\n\n    <el-table :data=\"list\" border style=\"width: 100%\" highlight-current-row>\n      <el-table-column label=\"名称\" prop=\"name\"></el-table-column>\n      <el-table-column label=\"类型\" prop=\"state_type\">\n        <template slot-scope=\"{ row }\">\n          <span>{{row.state_type|StateTypeFilter}}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"排序\" prop=\"order_id\"></el-table-column>\n      <el-table-column label=\"是否隐藏\" prop=\"is_hidden\">\n        <template slot-scope=\"{ row }\">\n          <el-tag v-if=\"row.is_hidden\" type=\"success\">是</el-tag>\n          <el-tag v-else type=\"danger\">否</el-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" width=\"260\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"{ row }\">\n          <el-button-group v-if=\"!row.is_hidden\">\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"small\"\n              type=\"primary\"\n              @click=\"handleUpdate(row)\"\n            >{{ \"编辑\" }}</el-button>\n            <el-button\n              v-if=\"permissionList.del\"\n              size=\"small\"\n              type=\"danger\"\n              @click=\"handleDelete(row)\"\n            >{{ \"删除\" }}</el-button>\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <el-dialog\n      :title=\"textMap[dialogStatus]\"\n      :visible.sync=\"dialogFormVisible\"\n      :close-on-click-modal=\"false\"\n    >\n      <el-form\n        ref=\"dataForm\"\n        :rules=\"rules\"\n        :model=\"temp\"\n        label-position=\"left\"\n        label-width=\"100px\"\n        style=\"margin-left:50px;\"\n      >\n        <el-form-item label=\"名称\" prop=\"name\">\n          <el-input v-model=\"temp.name\" />\n        </el-form-item>\n        <el-form-item label=\"隐藏\" prop=\"is_hidden\">\n          <el-switch v-model=\"temp.is_hidden\" active-color=\"#13ce66\" inactive-color=\"#ff4949\"></el-switch>\n        </el-form-item>\n        <el-form-item label=\"类型\" prop=\"state_type\">\n          <el-select v-model=\"temp.state_type\" clearable placeholder=\"请选择\">\n            <el-option\n              v-for=\"(label, value) in state_types\"\n              :key=\"value\"\n              :label=\"label\"\n              :value=\"parseInt(value)\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"排序\" prop=\"order_id\">\n          <el-input v-model=\"temp.order_id\" />\n        </el-form-item>\n        <el-form-item label=\"允许撤回\" prop=\"enable_retreat\">\n          <el-switch v-model=\"temp.enable_retreat\" active-color=\"#13ce66\" inactive-color=\"#ff4949\"></el-switch>\n          <a class=\"tips\">允许工单创建人在此状态直接撤回工单到初始状态</a>\n        </el-form-item>\n        <el-form-item label=\"参与者类型\" prop=\"participant_type\">\n          <el-select v-model=\"temp.participant_type\" placeholder=\"请选择\">\n            <el-option\n              v-for=\"(label, value) in participant_types\"\n              :key=\"value\"\n              :label=\"label\"\n              :value=\"value\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item v-if=\"temp.participant_type == 'user'\" label=\"参与用户\" prop=\"participant\">\n          <el-select v-model=\"temp.user_participant\" multiple filterable placeholder=\"请选择\">\n            <el-option\n              v-for=\"item in choice_user_list\"\n              :key=\"item.id\"\n              :label=\"item.username\"\n              :value=\"item.id\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item v-if=\"temp.participant_type == 'group'\" label=\"参与部门\" prop=\"participant\">\n          <el-select v-model=\"temp.group_participant\" multiple filterable placeholder=\"请选择\">\n            <el-option\n              v-for=\"item in choice_group_list\"\n              :key=\"item.id\"\n              :label=\"item.name\"\n              :value=\"item.id\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item v-if=\"temp.participant_type == 'role'\" label=\"参与角色\" prop=\"participant\">\n          <el-select v-model=\"temp.role_participant\" multiple filterable placeholder=\"请选择\">\n            <el-option\n              v-for=\"item in choice_role_list\"\n              :key=\"item.id\"\n              :label=\"item.name\"\n              :value=\"item.id\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"可編輯字段\" prop=\"fields\">\n          <el-transfer\n            v-model=\"temp.fields\"\n            filterable\n            :titles=\"['未选择', '已选择']\"\n            :data=\"customfield_list\"\n            :props=\"permprops\"\n          ></el-transfer>\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"memo\">\n          <el-input v-model=\"temp.memo\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogFormVisible = false\">{{ \"取消\" }}</el-button>\n        <el-button\n          type=\"primary\"\n          @click=\"dialogStatus === 'create' ? createData() : updateData()\"\n        >{{ \"确定\" }}</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { state, user, group, role, auth } from \"@/api/all\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"state\",\n  props: {\n    wfdata: {\n      type: Object,\n      default: {}\n    },\n    list: {\n      type: Array,\n      default: []\n    },\n    customfield_list: {\n      type: Array,\n      default: []\n    }\n  },\n\n  components: {},\n  data() {\n    return {\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false\n      },\n      temp: {},\n      dialogFormVisible: false,\n      dialogStatus: \"\",\n      textMap: {\n        update: \"编辑\",\n        create: \"添加\"\n      },\n      rules: {\n        name: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }]\n      },\n      state_types: {\n        0: \"普通状态\",\n        1: \"初始状态\",\n        2: \"结束状态\"\n      },\n      participant_types: {\n        none: \"none\",\n        user: \"用户\",\n        group: \"部门\",\n        role: \"角色\"\n      },\n      permprops: {\n        key: \"id\",\n        label: \"field_name\",\n        disabled: \"field_attribute\"\n      },\n      choice_user_list: [],\n      choice_group_list: [],\n      choice_role_list: []\n    };\n  },\n  computed: {},\n  created() {\n    this.getMenuButton();\n    this.selectParticipant();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"state\")\n        .then(response => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    handleFilter() {},\n    resetTemp() {\n      this.temp = {\n        memo: \"\",\n        name: \"\",\n        is_hidden: false,\n        order_id: undefined,\n        state_type: 0,\n        enable_retreat: false,\n        participant_type: \"none\",\n        user_participant: [],\n        group_participant: [],\n        role_participant: [],\n        fields: [],\n        workflow: this.wfdata.id\n      };\n    },\n    selectParticipant() {\n      user.requestGet().then(response => {\n        this.choice_user_list = response.results;\n      });\n      group.requestGet().then(response => {\n        this.choice_group_list = response.results;\n      });\n      role.requestGet().then(response => {\n        this.choice_role_list = response.results;\n      });\n    },\n    handleCreate() {\n      this.resetTemp();\n      this.dialogStatus = \"create\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    createData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          state\n            .requestPost(this.temp)\n            .then(response => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000\n              });\n              this.$emit(\"checkdata\");\n            })\n            .catch(() => {});\n        }\n      });\n    },\n    handleUpdate(row) {\n      this.temp = row;\n      this.dialogStatus = \"update\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    updateData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          state\n            .requestPut(this.temp.id, this.temp)\n            .then(() => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000\n              });\n              this.$emit(\"checkdata\");\n            })\n            .catch(() => {});\n        }\n      });\n    },\n    handleDelete(row) {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\"\n      })\n        .then(() => {\n          state.requestDelete(row.id).then(() => {\n            this.$message({\n              message: \"删除成功\",\n              type: \"success\"\n            });\n            this.$emit(\"checkdata\");\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\"\n          });\n        });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/workflow/pages/transition.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-button-group>\n        <el-button\n          v-if=\"permissionList.add\"\n          class=\"filter-item\"\n          type=\"success\"\n          icon=\"el-icon-edit\"\n          @click=\"handleCreate\"\n          >{{ \"添加\" }}</el-button\n        >\n      </el-button-group>\n    </div>\n\n    <el-table :data=\"list\" border style=\"width: 100%\" highlight-current-row>\n      <el-table-column label=\"名称\" prop=\"name\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.name | TransitionNameFilter }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"类型\" prop=\"transition_type\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.transition_type | TransitionTypeFilter }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"上一个节点\" prop=\"source_state\">\n        <template slot-scope=\"{ row }\">\n          <span v-if=\"row.source_state\">{{ row.source_state.name }}</span>\n          <span v-else>None</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"下一个节点\" prop=\"dest_state\">\n        <template slot-scope=\"{ row }\">\n          <span v-if=\"row.dest_state\">{{ row.dest_state.name }}</span>\n          <span v-else>None</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"属性类型\" prop=\"attribute_type\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.attribute_type | AttributeTypeFilter }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        width=\"260\"\n        class-name=\"small-padding fixed-width\"\n      >\n        <template slot-scope=\"{ row }\">\n          <el-button-group>\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"small\"\n              type=\"primary\"\n              @click=\"handleUpdate(row)\"\n              >{{ \"编辑\" }}</el-button\n            >\n            <el-button\n              v-if=\"permissionList.del\"\n              size=\"small\"\n              type=\"danger\"\n              @click=\"handleDelete(row)\"\n              >{{ \"删除\" }}</el-button\n            >\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <el-dialog\n      :title=\"textMap[dialogStatus]\"\n      :visible.sync=\"dialogFormVisible\"\n      :close-on-click-modal=\"false\"\n    >\n      <el-form\n        ref=\"dataForm\"\n        :rules=\"rules\"\n        :model=\"temp\"\n        label-position=\"left\"\n        label-width=\"100px\"\n        style=\"width: 400px; margin-left: 50px\"\n      >\n        <el-form-item label=\"名称\" prop=\"name\">\n          <el-select v-model=\"temp.name\" clearable placeholder=\"请选择\">\n            <el-option\n              v-for=\"(label, value) in transition_names\"\n              :key=\"value\"\n              :label=\"label\"\n              :value=\"parseInt(value)\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"类型\" prop=\"transition_type\">\n          <el-select\n            v-model=\"temp.transition_type\"\n            clearable\n            placeholder=\"请选择\"\n          >\n            <el-option\n              v-for=\"(label, value) in transition_types\"\n              :key=\"value\"\n              :label=\"label\"\n              :value=\"parseInt(value)\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"定时器\" prop=\"timer\">\n          <el-input v-model=\"temp.timer\" />‘\n          <a class=\"tips\"\n            >流转类型设置为定时器流转时生效,单位秒。处于源状态X秒后如果状态都没有过变化则自动流转到目标状态</a\n          >\n        </el-form-item>\n        <el-form-item label=\"属性类型\" prop=\"attribute_type\">\n          <el-select\n            v-model=\"temp.attribute_type\"\n            clearable\n            placeholder=\"请选择\"\n          >\n            <el-option\n              v-for=\"(label, value) in attribute_types\"\n              :key=\"value\"\n              :label=\"label\"\n              :value=\"parseInt(value)\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"上一个节点\" prop=\"source_state\">\n          <el-select v-model=\"temp.source_state\" clearable placeholder=\"请选择\">\n            <el-option\n              v-for=\"item in statedata\"\n              :key=\"item.id\"\n              :label=\"item.name\"\n              :value=\"parseInt(item.id)\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"下一个节点\" prop=\"dest_state\">\n          <el-select v-model=\"temp.dest_state\" clearable placeholder=\"请选择\">\n            <el-option\n              v-for=\"item in statedata\"\n              :key=\"item.id\"\n              :label=\"item.name\"\n              :value=\"parseInt(item.id)\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"条件表达式\" prop=\"condition_expression\">\n          <el-input v-model=\"temp.condition_expression\" />\n        </el-form-item>\n        <el-form-item label=\"点击弹窗提示\" prop=\"alert_enable\">\n          <el-switch\n            v-model=\"temp.alert_enable\"\n            active-color=\"#13ce66\"\n            inactive-color=\"#ff4949\"\n          ></el-switch>\n        </el-form-item>\n        <el-form-item label=\"弹窗内容\" prop=\"alert_text\">\n          <el-input v-model=\"temp.alert_text\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogFormVisible = false\">{{ \"取消\" }}</el-button>\n        <el-button\n          type=\"primary\"\n          @click=\"dialogStatus === 'create' ? createData() : updateData()\"\n          >{{ \"确定\" }}</el-button\n        >\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { transition, auth } from \"@/api/all\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate,\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"transition\",\n  props: {\n    wfdata: {\n      type: Object,\n      default: {},\n    },\n    statedata: {\n      type: Array,\n      default: [],\n    },\n    list: {\n      type: Array,\n      default: [],\n    },\n  },\n  components: {},\n  data() {\n    return {\n      aaa: this.statedata,\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false,\n      },\n      temp: {},\n      dialogFormVisible: false,\n      dialogStatus: \"\",\n      textMap: {\n        update: \"编辑\",\n        create: \"添加\",\n      },\n      rules: {\n        name: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }],\n      },\n      transition_types: {\n        0: \"常规流转\",\n        1: \"定时器流转\",\n      },\n      attribute_types: {\n        0: \"草稿\",\n        1: \"待审\",\n        2: \"驳回\",\n        3: \"撤销\",\n        4: \"结束\",\n        5: \"已关闭\",\n      },\n      transition_names: {\n        0: \"保存\",\n        1: \"转交下一步\",\n        2: \"驳回\",\n        3: \"撤销\",\n        4: \"关闭\",\n      },\n    };\n  },\n  computed: {},\n  created() {\n    this.getMenuButton();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"transition\")\n        .then((response) => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    handleFilter() {},\n    resetTemp() {\n      this.temp = {\n        memo: \"\",\n        name: \"\",\n        transition_type: 0,\n        timer: 0,\n        condition_expression: \"[]\",\n        attribute_type: 0,\n        alert_enable: false,\n        alert_text: \"\",\n        source_state: undefined,\n        dest_state: undefined,\n        workflow: this.wfdata.id,\n      };\n    },\n    handleCreate() {\n      this.resetTemp();\n      this.dialogStatus = \"create\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    createData() {\n      this.$refs[\"dataForm\"].validate((valid) => {\n        if (valid) {\n          this.temp.workflow = this.wfdata.id;\n          transition\n            .requestPost(this.temp)\n            .then((response) => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000,\n              });\n              this.$emit(\"checkdata\");\n            })\n            .catch(() => {});\n        }\n      });\n    },\n    handleUpdate(row) {\n      this.temp = Object.assign({}, row, {\n        source_state: row.source_state.id,\n        dest_state: row.dest_state.id,\n        workflow: this.wfdata.id,\n      });\n      this.dialogStatus = \"update\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    updateData() {\n      this.$refs[\"dataForm\"].validate((valid) => {\n        if (valid) {\n          transition\n            .requestPut(this.temp.id, this.temp)\n            .then(() => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000,\n              });\n              this.$emit(\"checkdata\");\n            })\n            .catch(() => {});\n        }\n      });\n    },\n    handleDelete(row) {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\",\n      })\n        .then(() => {\n          transition.requestDelete(row.id).then(() => {\n            this.$message({\n              message: \"删除成功\",\n              type: \"success\",\n            });\n            this.$emit(\"checkdata\");\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\",\n          });\n        });\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/workflow/wfconf.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-card>\n      <el-steps>\n        <el-step v-for=\"item in state_list\" :key=\"item.id\" status=\"finish\" :title=\"item.name\" v-if=\"!item.is_hidden\"></el-step>\n      </el-steps>\n    </el-card>\n\n    <el-tabs v-model=\"activeName\" style=\"margin-top:15px;\" type=\"border-card\">\n      <el-tab-pane label=\"工作流字段\" name=\"customfield\">\n        <span slot=\"label\"><i class=\"el-icon-lollipop\"></i> 工作流字段</span>\n        <keep-alive>\n          <tab-customfield\n            @checkdata=\"getCustomfieldList\"\n            :wfdata=\"wfdata\"\n            :list=\"customfield_list\"\n          ></tab-customfield>\n        </keep-alive>\n      </el-tab-pane>\n\n      <el-tab-pane label=\"工作流节点\" name=\"state\">\n        <span slot=\"label\"><i class=\"el-icon-ice-cream-square\"></i> 工作流节点</span>\n        <keep-alive>\n          <tab-state\n            @checkdata=\"getStateList\"\n            :wfdata=\"wfdata\"\n            :list=\"state_list\"\n            :customfield_list=\"customfield_list\"\n          ></tab-state>\n        </keep-alive>\n      </el-tab-pane>\n\n      <el-tab-pane label=\"工作流步骤\" name=\"transition\">\n        <span slot=\"label\"><i class=\"el-icon-ice-cream-round\"></i> 工作流步骤</span>\n        <keep-alive>\n          <tab-transition\n            @checkdata=\"getTransitionList\"\n            :wfdata=\"wfdata\"\n            :statedata=\"state_list\"\n            :list=\"transition_list\"\n          ></tab-transition>\n        </keep-alive>\n      </el-tab-pane>\n    </el-tabs>\n  </div>\n</template>\n\n<script>\nimport { workflow, customfield, state, transition, auth } from \"@/api/all\";\nimport tabCustomfield from \"./pages/customfield\";\nimport tabState from \"./pages/state\";\nimport tabTransition from \"./pages/transition\";\n\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"wfconf\",\n\n  components: { tabCustomfield, tabState, tabTransition },\n  data() {\n    return {\n      activeName: \"customfield\",\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false\n      },\n      tempRoute: {},\n      wfdata: {},\n      customfield_list: [],\n      state_list: [],\n      transition_list: [],\n      temp: {\n        workflow: undefined,\n      },\n    };\n  },\n\n  created() {\n    const id = this.$route.params && this.$route.params.id;\n    this.fetchData(id);\n    this.tempRoute = Object.assign({}, this.$route);\n  },\n  methods: {\n    fetchData(id) {\n      this.temp.workflow = id;\n      const params = {\n        id: id\n      };\n      workflow.requestGet(params).then(response => {\n        this.wfdata = response.results[0];\n        this.setPageTitle();\n        this.getCustomfieldList();\n        this.getStateList();\n        this.getTransitionList();\n      });\n    },\n    getCustomfieldList() {\n      customfield.requestGet(this.temp).then(response => {\n        this.customfield_list = response.results;\n      });\n    },\n    getStateList() {\n      state.requestGet(this.temp).then(response => {\n        this.state_list = response.results;\n      });\n    },\n    getTransitionList() {\n      transition.requestGet(this.temp).then(response => {\n        this.transition_list = response.results;\n      });\n    },\n    handleFilter() {\n      this.fetchData();\n    },\n    setPageTitle() {\n      const title = this.wfdata.name;\n      document.title = `${title} - 配置`;\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/workflow/wfset.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-input\n        v-model=\"listQuery.search\"\n        placeholder=\"请输入内容\"\n        clearable\n        prefix-icon=\"el-icon-search\"\n        style=\"width: 200px\"\n        class=\"filter-item\"\n        @keyup.enter.native=\"handleFilter\"\n        @clear=\"handleFilter\"\n      />\n      <el-button-group>\n        <el-button\n          class=\"filter-item\"\n          type=\"primary\"\n          icon=\"el-icon-search\"\n          @click=\"handleFilter\"\n          >{{ \"搜索\" }}</el-button\n        >\n        <el-button\n          v-if=\"permissionList.add\"\n          class=\"filter-item\"\n          type=\"success\"\n          icon=\"el-icon-edit\"\n          @click=\"handleCreate\"\n          >{{ \"添加\" }}</el-button\n        >\n      </el-button-group>\n    </div>\n\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      border\n      style=\"width: 100%\"\n      highlight-current-row\n      @sort-change=\"handleSortChange\"\n    >\n      <el-table-column label=\"名称\" prop=\"name\"></el-table-column>\n      <el-table-column label=\"类型\" prop=\"type\">\n        <template slot-scope=\"{ row }\">\n          <span>{{ row.type.name }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"工单流水号前缀\"\n        prop=\"ticket_sn_prefix\"\n      ></el-table-column>\n      <el-table-column label=\"状态\" prop=\"status\" sortable=\"custom\">\n        <template slot-scope=\"{ row }\">\n          <el-tag v-if=\"row.status\" type=\"success\">启用</el-tag>\n          <el-tag v-else type=\"danger\">禁用</el-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"查看权限校验\"\n        prop=\"view_permission_check\"\n        sortable=\"custom\"\n      >\n        <template slot-scope=\"{ row }\">\n          <el-tag v-if=\"row.view_permission_check\" type=\"success\">启用</el-tag>\n          <el-tag v-else type=\"danger\">禁用</el-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        width=\"260\"\n        class-name=\"small-padding fixed-width\"\n      >\n        <template slot-scope=\"{ row }\">\n          <el-button-group>\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"small\"\n              type=\"primary\"\n              @click=\"handleUpdate(row)\"\n              >{{ \"编辑\" }}</el-button\n            >\n            <el-button\n              v-if=\"permissionList.del\"\n              size=\"small\"\n              type=\"danger\"\n              @click=\"handleDelete(row)\"\n              >{{ \"删除\" }}</el-button\n            >\n            <router-link :to=\"'/wfconf/' + row.id\">\n              <el-button\n                v-if=\"permissionList.update\"\n                size=\"small\"\n                type=\"warning\"\n                >{{ \"配置\" }}</el-button\n              >\n            </router-link>\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <div class=\"table-pagination\">\n      <pagination\n        v-show=\"total > 0\"\n        :total=\"total\"\n        :page.sync=\"listQuery.page\"\n        :limit.sync=\"listQuery.limit\"\n        @pagination=\"getList\"\n      />\n    </div>\n    <el-dialog\n      :title=\"textMap[dialogStatus]\"\n      :visible.sync=\"dialogFormVisible\"\n      :close-on-click-modal=\"false\"\n    >\n      <el-form\n        ref=\"dataForm\"\n        :rules=\"rules\"\n        :model=\"temp\"\n        label-position=\"left\"\n        label-width=\"100px\"\n        style=\"width: 400px; margin-left: 50px\"\n      >\n        <el-form-item label=\"名称\" prop=\"name\">\n          <el-input v-model=\"temp.name\" />\n        </el-form-item>\n        <el-form-item label=\"工单号前缀\" prop=\"ticket_sn_prefix\">\n          <el-input v-model=\"temp.ticket_sn_prefix\" />\n        </el-form-item>\n        <el-form-item label=\"工单类型\" prop=\"type\">\n          <el-select v-model=\"temp.type\" placeholder=\"请选择\">\n            <el-option\n              v-for=\"item in wftype_list\"\n              :key=\"item.id\"\n              :label=\"item.name\"\n              :value=\"item.id\"\n            ></el-option>\n          </el-select>\n        </el-form-item>\n        <el-form-item label=\"状态\" prop=\"status\">\n          <el-switch\n            v-model=\"temp.status\"\n            active-color=\"#13ce66\"\n            inactive-color=\"#ff4949\"\n          ></el-switch>\n        </el-form-item>\n        <el-form-item label=\"角色\" prop=\"roles\">\n          <el-tree\n            ref=\"tree\"\n            :check-strictly=\"false\"\n            :data=\"treeData\"\n            :props=\"treeProps\"\n            show-checkbox\n            accordion\n            default-expand-all\n            node-key=\"id\"\n            class=\"permission-tree\"\n          />\n        </el-form-item>\n        <el-form-item label=\"查看权限校验\" prop=\"view_permission_check\">\n          <el-switch\n            v-model=\"temp.view_permission_check\"\n            active-color=\"#13ce66\"\n            inactive-color=\"#ff4949\"\n          ></el-switch>\n        </el-form-item>\n        <el-form-item label=\"限制表达式\" prop=\"limit_expression\">\n          <el-input\n            type=\"textarea\"\n            :autosize=\"{ minRows: 2, maxRows: 4 }\"\n            placeholder=\"限制周期({'period':24} 24小时)\"\n            v-model=\"temp.limit_expression\"\n          />\n        </el-form-item>\n        <el-form-item label=\"展现表单字段\" prop=\"display_form_str\">\n          <el-input\n            type=\"textarea\"\n            :autosize=\"{ minRows: 2, maxRows: 4 }\"\n            placeholder=\"['name','sn']\"\n            v-model=\"temp.display_form_str\"\n          />\n        </el-form-item>\n        <el-form-item label=\"标题模板\" prop=\"title_template\">\n          <el-input\n            type=\"textarea\"\n            :autosize=\"{ minRows: 2, maxRows: 4 }\"\n            placeholder=\"你有一个待办工单:{name}\"\n            v-model=\"temp.title_template\"\n          />\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"memo\">\n          <el-input v-model=\"temp.memo\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogFormVisible = false\">{{ \"取消\" }}</el-button>\n        <el-button\n          type=\"primary\"\n          @click=\"dialogStatus === 'create' ? createData() : updateData()\"\n          >{{ \"确定\" }}</el-button\n        >\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { workflow, workflowtype, role, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate,\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"wfset\",\n\n  components: { Pagination },\n  data() {\n    return {\n      valueIdSelectTree: 0,\n      propsSelectTree: {\n        value: \"id\",\n        label: \"name\",\n        children: \"children\",\n        placeholder: \"父级\",\n      },\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false,\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      loading: true,\n      listQuery: {\n        page: 1,\n        limit: 20,\n        search: undefined,\n        ordering: undefined,\n      },\n      temp: {},\n      dialogFormVisible: false,\n      dialogStatus: \"\",\n      textMap: {\n        update: \"编辑\",\n        create: \"添加\",\n      },\n      rules: {\n        name: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }],\n        type: [{ required: true, message: \"请选择类型\", trigger: \"change\" }],\n        ticket_sn_prefix: [\n          { required: true, message: \"请输入工单流水号前缀\", trigger: \"blur\" },\n        ],\n      },\n      treeProps: {\n        children: \"children\",\n        label: \"name\",\n      },\n      treeData: [],\n      wftype_list: [],\n    };\n  },\n  computed: {},\n  created() {\n    this.getMenuButton();\n    this.getList();\n    this.getWftypeList();\n    this.getTreeData();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"wfset\")\n        .then((response) => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      workflow.requestGet(this.listQuery).then((response) => {\n        this.list = response.results;\n        this.total = response.count;\n        this.listLoading = false;\n      });\n    },\n    getWftypeList() {\n      workflowtype.requestGet().then((response) => {\n        this.wftype_list = response.results;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    },\n    resetTemp() {\n      this.temp = {\n        name: \"\",\n        ticket_sn_prefix: \"xxoo\",\n        type: \"\",\n        status: true,\n        rolse: [],\n        view_permission_check: true,\n        limit_expression: \"\",\n        display_form_str: \"\",\n        title_template: \"\",\n        content_template: \"\",\n        memo: \"\",\n      };\n    },\n    handleCreate() {\n      this.resetTemp();\n      this.dialogStatus = \"create\";\n      this.dialogFormVisible = true;\n      this.loading = false;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    createData() {\n      this.$refs[\"dataForm\"].validate((valid) => {\n        if (valid) {\n          this.loading = true;\n          this.temp.roles = this.$refs.tree.getCheckedKeys(true);\n          workflow\n            .requestPost(this.temp)\n            .then((response) => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000,\n              });\n              this.getList();\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleUpdate(row) {\n      this.temp = row;\n      this.temp = Object.assign({}, this.temp, {\n        type: this.temp.type.id,\n        roles: row.roles.map(a => a.id),\n      });\n      this.dialogStatus = \"update\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n        this.$refs.tree.setCheckedKeys(this.temp.roles);\n      });\n    },\n    updateData() {\n      this.$refs[\"dataForm\"].validate((valid) => {\n        if (valid) {\n          this.loading = true;\n          this.temp.roles = this.$refs.tree.getCheckedKeys(true);\n          workflow\n            .requestPut(this.temp.id, this.temp)\n            .then(() => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000,\n              });\n              this.getList();\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleDelete(row) {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\",\n      })\n        .then(() => {\n          workflow.requestDelete(row.id).then(() => {\n            this.$message({\n              message: \"删除成功\",\n              type: \"success\",\n            });\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\",\n          });\n        });\n    },\n    getTreeData() {\n      role.requestGet().then((response) => {\n        this.treeData = this.optionDataSelectTree(response.results);\n      });\n    },\n    optionDataSelectTree(data) {\n      const cloneData = data;\n      return cloneData.filter((father) => {\n        const branchArr = cloneData.filter(\n          (child) => father.id === child.parent\n        );\n        branchArr.length > 0 ? (father.children = branchArr) : \"\";\n        return father.parent === data[0].parent;\n      });\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/workflow/wftype.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div class=\"filter-container\">\n      <el-input\n        v-model=\"listQuery.search\"\n        placeholder=\"请输入内容\"\n        clearable\n        prefix-icon=\"el-icon-search\"\n        style=\"width: 200px;\"\n        class=\"filter-item\"\n        @keyup.enter.native=\"handleFilter\"\n        @clear=\"handleFilter\"\n      />\n      <el-button-group>\n        <el-button\n          class=\"filter-item\"\n          type=\"primary\"\n          icon=\"el-icon-search\"\n          @click=\"handleFilter\"\n        >{{ \"搜索\" }}</el-button>\n        <el-button\n          v-if=\"permissionList.add\"\n          class=\"filter-item\"\n          type=\"success\"\n          icon=\"el-icon-edit\"\n          @click=\"handleCreate\"\n        >{{ \"添加\" }}</el-button>\n      </el-button-group>\n    </div>\n\n    <el-table\n      :data=\"list\"\n      v-loading=\"listLoading\"\n      border\n      style=\"width: 100%\"\n      highlight-current-row\n      @sort-change=\"handleSortChange\"\n    >\n      <el-table-column label=\"名称\" prop=\"name\"></el-table-column>\n      <el-table-column label=\"code\" prop=\"code\"></el-table-column>\n      <el-table-column label=\"状态\" prop=\"status\" sortable=\"custom\">\n        <template slot-scope=\"{ row }\">\n          <el-tag v-if=\"row.status\" type=\"success\">启用</el-tag>\n          <el-tag v-else type=\"danger\">禁用</el-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"顺序\" prop=\"order_id\"></el-table-column>\n      <el-table-column label=\"操作\" align=\"center\" width=\"260\" class-name=\"small-padding fixed-width\">\n        <template slot-scope=\"{ row }\">\n          <el-button-group>\n            <el-button\n              v-if=\"permissionList.update\"\n              size=\"small\"\n              type=\"primary\"\n              @click=\"handleUpdate(row)\"\n            >{{ \"编辑\" }}</el-button>\n            <el-button\n              v-if=\"permissionList.del\"\n              size=\"small\"\n              type=\"danger\"\n              @click=\"handleDelete(row)\"\n            >{{ \"删除\" }}</el-button>\n          </el-button-group>\n        </template>\n      </el-table-column>\n    </el-table>\n    <div class=\"table-pagination\">\n      <pagination\n        v-show=\"total > 0\"\n        :total=\"total\"\n        :page.sync=\"listQuery.page\"\n        :limit.sync=\"listQuery.limit\"\n        @pagination=\"getList\"\n      />\n    </div>\n    <el-dialog\n      :title=\"textMap[dialogStatus]\"\n      :visible.sync=\"dialogFormVisible\"\n      :close-on-click-modal=\"false\"\n    >\n      <el-form\n        ref=\"dataForm\"\n        :rules=\"rules\"\n        :model=\"temp\"\n        label-position=\"left\"\n        label-width=\"100px\"\n        style=\"width: 400px; margin-left:50px;\"\n      >\n        <el-form-item label=\"名称\" prop=\"name\">\n          <el-input v-model=\"temp.name\" />\n        </el-form-item>\n        <el-form-item label=\"code\" prop=\"code\">\n          <el-input v-model=\"temp.code\" />\n        </el-form-item>\n        <el-form-item label=\"状态\" prop=\"status\">\n          <el-switch v-model=\"temp.status\" active-color=\"#13ce66\" inactive-color=\"#ff4949\"></el-switch>\n        </el-form-item>\n        <el-form-item label=\"顺序\" prop=\"order_id\">\n          <el-input v-model=\"temp.order_id\" />\n        </el-form-item>\n        <el-form-item label=\"备注\" prop=\"memo\">\n          <el-input v-model=\"temp.memo\" />\n        </el-form-item>\n      </el-form>\n      <div slot=\"footer\" class=\"dialog-footer\">\n        <el-button @click=\"dialogFormVisible = false\">{{ \"取消\" }}</el-button>\n        <el-button\n          type=\"primary\"\n          @click=\"dialogStatus === 'create' ? createData() : updateData()\"\n        >{{ \"确定\" }}</el-button>\n      </div>\n    </el-dialog>\n  </div>\n</template>\n\n<script>\nimport { workflowtype, auth } from \"@/api/all\";\nimport Pagination from \"@/components/Pagination\";\nimport {\n  checkAuthAdd,\n  checkAuthDel,\n  checkAuthView,\n  checkAuthUpdate\n} from \"@/utils/permission\";\n\nexport default {\n  name: \"wfset\",\n\n  components: { Pagination },\n  data() {\n    return {\n      operationList: [],\n      permissionList: {\n        add: false,\n        del: false,\n        view: false,\n        update: false\n      },\n      list: [],\n      total: 0,\n      listLoading: true,\n      loading: true,\n      listQuery: {\n        page: 1,\n        limit: 20,\n        search: undefined,\n        ordering: undefined\n      },\n      temp: {},\n      dialogFormVisible: false,\n      dialogStatus: \"\",\n      textMap: {\n        update: \"编辑\",\n        create: \"添加\"\n      },\n      rules: {\n        name: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }]\n      }\n    };\n  },\n  computed: {},\n  created() {\n    this.getMenuButton();\n    this.getList();\n  },\n  methods: {\n    checkPermission() {\n      this.permissionList.add = checkAuthAdd(this.operationList);\n      this.permissionList.del = checkAuthDel(this.operationList);\n      this.permissionList.view = checkAuthView(this.operationList);\n      this.permissionList.update = checkAuthUpdate(this.operationList);\n    },\n    getMenuButton() {\n      auth\n        .requestMenuButton(\"wfset\")\n        .then(response => {\n          this.operationList = response.results;\n        })\n        .then(() => {\n          this.checkPermission();\n        });\n    },\n    getList() {\n      this.listLoading = true;\n      workflowtype.requestGet(this.listQuery).then(response => {\n        this.list = response.results;\n        this.total = response.count;\n        this.listLoading = false;\n      });\n    },\n    handleFilter() {\n      this.getList();\n    },\n    handleSortChange(val) {\n      if (val.order === \"ascending\") {\n        this.listQuery.ordering = val.prop;\n      } else if (val.order === \"descending\") {\n        this.listQuery.ordering = \"-\" + val.prop;\n      } else {\n        this.listQuery.ordering = \"\";\n      }\n      this.getList();\n    },\n    resetTemp() {\n      this.temp = {\n        name: \"\",\n        code: \"\",\n        status: true,\n        order_id: \"\",\n        memo: \"\"\n      };\n    },\n    handleCreate() {\n      this.resetTemp();\n      this.dialogStatus = \"create\";\n      this.dialogFormVisible = true;\n      this.loading = false;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    createData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          workflowtype\n            .requestPost(this.temp)\n            .then(response => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"创建成功\",\n                type: \"success\",\n                duration: 2000\n              });\n              this.getList();\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleUpdate(row) {\n      this.temp = row;\n      this.dialogStatus = \"update\";\n      this.dialogFormVisible = true;\n      this.$nextTick(() => {\n        this.$refs[\"dataForm\"].clearValidate();\n      });\n    },\n    updateData() {\n      this.$refs[\"dataForm\"].validate(valid => {\n        if (valid) {\n          this.loading = true;\n          workflowtype\n            .requestPut(this.temp.id, this.temp)\n            .then(() => {\n              this.dialogFormVisible = false;\n              this.$notify({\n                title: \"成功\",\n                message: \"更新成功\",\n                type: \"success\",\n                duration: 2000\n              });\n              this.getList();\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        }\n      });\n    },\n    handleDelete(row) {\n      this.$confirm(\"是否确定删除?\", \"提示\", {\n        confirmButtonText: \"确定\",\n        cancelButtonText: \"取消\",\n        type: \"warning\"\n      })\n        .then(() => {\n          workflowtype.requestDelete(row.id).then(() => {\n            this.$message({\n              message: \"删除成功\",\n              type: \"success\"\n            });\n            this.getList();\n          });\n        })\n        .catch(() => {\n          this.$message({\n            type: \"info\",\n            message: \"已取消删除\"\n          });\n        });\n    }\n  }\n};\n</script>\n"
  },
  {
    "path": "frontend/vue.config.js",
    "content": "'use strict'\nconst path = require('path')\nconst defaultSettings = require('./src/settings.js')\n// const i18n = require('./src/lang/index.js')\n\nfunction resolve(dir) {\n  return path.join(__dirname, dir)\n}\n\nconst name = defaultSettings.title || 'vue Element Admin' // page title\n// const name = i18n.t('systemTitle')\n\n// If your port is set to 80,\n// use administrator privileges to execute the command line.\n// For example, Mac: sudo npm run\n// You can change the port by the following method:\n// port = 9527 npm run dev OR npm run dev --port = 9527\nconst port = process.env.port || process.env.npm_config_port || 9527 // dev port\n\n// All configuration item explanations can be find in https://cli.vuejs.org/config/\nmodule.exports = {\n  /**\n   * You will need to set publicPath if you plan to deploy your site under a sub path,\n   * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,\n   * then publicPath should be set to \"/bar/\".\n   * In most cases please use '/' !!!\n   * Detail: https://cli.vuejs.org/config/#publicpath\n   */\n  publicPath: '/',\n  outputDir: 'dist',\n  assetsDir: 'static',\n  lintOnSave: process.env.NODE_ENV === 'development',\n  productionSourceMap: false,\n  devServer: {\n    host: '127.0.0.1',\n    port: 8080,\n    open: true,\n    overlay: {\n      warnings: false,\n      errors: true\n    },\n    public: 'http://127.0.0.1:8080',  // 本地ip\n    proxy: {\n      '/api/':{\n        target:'http://127.0.0.1:8000',\n        changeOrigin:true\n      }\n    }\n  },\n  configureWebpack: {\n    // provide the app's title in webpack's name field, so that\n    // it can be accessed in index.html to inject the correct title.\n    name: name,\n    resolve: {\n      alias: {\n        '@': resolve('src')\n      }\n    }\n  },\n  chainWebpack(config) {\n    config.plugins.delete('preload') // TODO: need test\n    config.plugins.delete('prefetch') // TODO: need test\n\n    // set svg-sprite-loader\n    config.module\n      .rule('svg')\n      .exclude.add(resolve('src/icons'))\n      .end()\n    config.module\n      .rule('icons')\n      .test(/\\.svg$/)\n      .include.add(resolve('src/icons'))\n      .end()\n      .use('svg-sprite-loader')\n      .loader('svg-sprite-loader')\n      .options({\n        symbolId: 'icon-[name]'\n      })\n      .end()\n\n    // set preserveWhitespace\n    config.module\n      .rule('vue')\n      .use('vue-loader')\n      .loader('vue-loader')\n      .tap(options => {\n        options.compilerOptions.preserveWhitespace = true\n        return options\n      })\n      .end()\n\n    config\n      .when(process.env.NODE_ENV !== 'development',\n        config => {\n          config\n            .plugin('ScriptExtHtmlWebpackPlugin')\n            .after('html')\n            .use('script-ext-html-webpack-plugin', [{\n            // `runtime` must same as runtimeChunk name. default is `runtime`\n              inline: /runtime\\..*\\.js$/\n            }])\n            .end()\n          config\n            .optimization.splitChunks({\n              chunks: 'all',\n              cacheGroups: {\n                libs: {\n                  name: 'chunk-libs',\n                  test: /[\\\\/]node_modules[\\\\/]/,\n                  priority: 10,\n                  chunks: 'initial' // only package third parties that are initially dependent\n                },\n                elementUI: {\n                  name: 'chunk-elementUI', // split elementUI into a single package\n                  priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app\n                  test: /[\\\\/]node_modules[\\\\/]_?element-ui(.*)/ // in order to adapt to cnpm\n                },\n                commons: {\n                  name: 'chunk-commons',\n                  test: resolve('src/components'), // can customize your rules\n                  minChunks: 3, //  minimum common number\n                  priority: 5,\n                  reuseExistingChunk: true\n                }\n              }\n            })\n          config.optimization.runtimeChunk('single')\n        }\n      )\n  }\n}\n"
  },
  {
    "path": "oms_nginx.conf",
    "content": "upstream django {\n    server unix:/data/app/one/backend/core.sock; # for a file socket\n}\n\nserver {\n    listen      80;\n    server_name oms.itimor.com;\n    root /data/app/one/;\n\n    charset utf-8;\n    client_max_body_size 200m;\n\n    error_page 404 500 502 = @502_page;\n\n    location @502_page {\n        rewrite ^(.*)$ /500.html break;\n    }\n\n    location /static/ {\n        alias /data/app/one/frontend/dist/static/;\n    }\n\n    location / {\n        uwsgi_pass  django;\n        include     uwsgi_params;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \"upgrade\";\n        proxy_read_timeout  36000s;\n        proxy_send_timeout  36000s;\n    }\n}"
  }
]