[
  {
    "path": "README.md",
    "content": "# Yolov8-flask-vue（本科毕设）\n\n这是一个基于ultralytics的一个部署到flask后端，然后vue作为前端所展示的一个通用的Yolo目标检测的展示页面，其实本质上类似于有着web页面外观的本地exe项目（因为数据库是个本地文件，放在sqlite上）\n\n需要在pycharm等IDE安装好sqlite相关连接\n\n基本上只要是ultralytics训练好的模型都可以运行，也就是说官方提供的COCO训练好的模型都可以在这个Yolo通用前端平台上使用\n\n后端需要安装好环境才能运行，要注意先运行后端再是前端，因为前端的环境是很容易装好的\n\n前端需要安装好Node.js到电脑上，然后只需要将，只需要在WebStorm上等随意的IDE点击 package.json 上的播放按钮就可以了\n\n![img.png](img/img.png)\n\n\n后端是flask，要安装很多的库，先安装好相应的库，IDE会提示python那些库没有安装上去的\n\n\n\n这是毕设模型变体的训练过程，其中comet的Yolov5su当中因为VPN不稳定中间断了一些就不显示了\n\n![img.png](img/mAP50.png)\n\n个人毕设训练这些模型不方便公开，这里只是提供了一个通用的YOLO实现平台，可以将自己训练好的模型放入到上面去来实现目标检测展示功能\n\n在这个毕设里面一共是200多张图片，6个yolo模型，拉下来需要一定时间\n\n图片上传来自于本地或者是url地址，但url地址有着严格要求\n\n图片上传后不能修改名称，除非删除再上传，同理，模型也是如此\n\n\n### 展示效果\n\n![img.png](img/show3.png)\n\n![img.png](img/show.png)\n![img.png](img/show2.png)\n![img.png](img/show4.png)\n\n### 个人环境\n\n#### 开发技术及工具的选择：\n\nWindows 11\n\nPython 3.9.13\n\nPytorch 1.13.1+cu117\n\nJetbrains-IDEA系列2022\n\n#### 开发环境：\n\n数据库：使用的是sqlite本地的\n\n硬件：电脑内存32G左右，GPU RTX A5000\n\nWindows 11以上操作系统；\n\n软件： Anaconda22.9.0 + python3.9.13 + Pytorch1.13.1+cu117 + Pycharm(或者是DataSpell) + WebStorm + Node.js 18.14.1 (x64)\n\n### 低配部署环境：\n\n硬件：电脑内存16G左右，GPU RTX 1050Ti\n\nWindows 10以上操作系统；\n\n软件： Pycharm(或者是DataSpell) + WebStorm + Node.js 18.14.1 (x64) + flask版本2.2左右\n\n### 后续说明\n\n本人现在忙着准备深大的研究生复试（❌），差一名，初试分数太低了（不到320）\n\n备考二战，初试结束，今年11408是目前最难的一年，个人觉得408以后难度还会刷新\n\n毕业论文已经完成\n\n现在Yolov8库的更新情况不知道怎么样了，这个项目有半年没碰过了。。\n\n后面会对这个项目细节相关进行说明\n\n\n### 感谢 & 技术提供\n\nultralytics官网: https://github.com/ultralytics/ultralytics\n\n![img.png](img/img9.png)\n\n![img.png](img/img2.png)\n\n实验数据记录: https://www.comet.com/site/lp/yolov5-with-comet/?utm_source=yolov5&utm_medium=partner&utm_campaign=partner_yolov5_2022&utm_content=github\n\n![img.png](img/img6.png)\n\n前端提供:\nhttps://github.com/lin-xin/vue-manage-system\n\n\n\n"
  },
  {
    "path": "detection-backend/api.py",
    "content": "import os\nimport shutil\n\nfrom flask.views import MethodView, request\nfrom models import File, WModel, Object, Result\nfrom extension import db\nfrom config import save_path, \\\n    result_path, img_url_show, result_url_show, cache_save_model_path, weights_path\n\n\nclass FileApi(MethodView):\n    \"\"\"\n    后端接口部分 RESTful API风格\n    \"\"\"\n\n    def __init__(self):\n        super(FileApi, self).__init__()\n\n    def get(self, fid):\n        \"\"\"\n        查询数据\n        :param fid: 文件id\n        :return: json\n        \"\"\"\n        try:\n            if not fid:\n                files: [File] = File.query.all()\n                results = [\n                    {\n                        'fid': file.fid,\n                        'name': file.name,\n                        'timestamp': file.timestamp,\n                        'type': file.type,\n                        'origin': file.origin,\n                        'width': file.width,\n                        'height': file.height\n                    }\n                    for file in files\n                ]\n\n            else:\n                file = File.query.get(fid)\n                results = {\n                    'fid': file.fid,\n                    'name': file.name,\n                    'timestamp': file.timestamp,\n                    'type': file.type,\n                    'origin': file.origin,\n                    'width': file.width,\n                    'height': file.height\n                }\n\n            return {\n                'status': 'success',\n                'message': '数据查询成功',\n                'results': results\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据查询出现异常',\n                'info': str(e)\n            }\n\n    def delete(self, fid):\n        \"\"\"\n        删除操作\n        :param fid: 文件id\n        :return: json\n        \"\"\"\n        try:\n            file: File = File.query.get(fid)\n            db.session.delete(file)\n            db.session.commit()\n\n            return {\n                'status': 'success',\n                'message': '数据删除成功',\n            }\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据删除出现异常',\n                'info': str(e)\n            }\n\n    def post(self):  # 添加数据\n        \"\"\"\n        添加操作\n        :return: json\n        \"\"\"\n        try:\n            height = request.json.get(\"height\")\n            width = request.json.get(\"width\")\n            name = request.json.get(\"name\")\n            type = request.json.get(\"type\")\n            origin = request.json.get(\"origin\")\n\n            file = File()\n\n            file.name = name\n            file.height = height\n            file.width = width\n            file.type = type\n            file.origin = origin\n\n            db.session.add(file)\n            db.session.commit()\n\n            return {\n                'status': 'success',\n                'message': '数据添加成功'\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据添加出现异常',\n                'info': str(e)\n            }\n\n\nclass WModelApi(MethodView):\n    \"\"\"\n    后端接口部分 RESTful API风格\n    \"\"\"\n\n    def __init__(self):\n        super(WModelApi, self).__init__()\n\n    def get(self, wid):\n        \"\"\"\n        查询数据\n        :param wid: 模型的wid\n        :return: json\n        \"\"\"\n        try:\n            if not wid:\n                wmodels: [WModel] = WModel.query.all()\n                results = [\n                    {\n                        'wid': wmodel.wid,\n                        'model_name': wmodel.model_name,\n                        'model_type': wmodel.model_type,\n                        'model_dataset': wmodel.model_dataset,\n                    }\n                    for wmodel in wmodels\n                ]\n\n            else:\n                wmodel: WModel = WModel.query.get(wid)\n                results = {\n                    'wid': wmodel.wid,\n                    'model_name': wmodel.model_name,\n                    'model_type': wmodel.model_type,\n                    'model_dataset': wmodel.model_dataset,\n                }\n\n            return {\n                'status': 'success',\n                'message': '数据查询成功',\n                'results': results\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据查询出现异常',\n                'info': str(e)\n            }\n\n    def delete(self, wid):\n        \"\"\"\n        删除操作\n        :param rid: 结果id\n        :return: json\n        \"\"\"\n        try:\n            if wid > 6:\n                wmodel: WModel = WModel.query.get(wid)\n\n                delete_path = weights_path + wmodel.model_name + '.pt'\n                os.remove(delete_path)\n\n                db.session.delete(wmodel)\n                db.session.commit()\n\n                return {\n                    'status': 'success',\n                    'message': '数据删除成功',\n                }\n\n            else:\n                return {\n                    'status': 'error',\n                    'message': '不允许删除前六个模型',\n                }\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据删除出现异常',\n                'info': str(e)\n            }\n\n    def put(self, wid):\n        \"\"\"\n        修改模型信息\n        :param wid: 模型id\n        :return: json\n        \"\"\"\n        try:\n            model: WModel = WModel.query.get(wid)\n            model.model_name = request.json.get('model_name')\n            model.model_type = request.json.get('model_type')\n            model.model_dataset = request.json.get('model_dataset')\n\n            db.session.commit()\n\n            return {\n                'status': 'success',\n                'message': '数据修改成功'\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据修改出现异常',\n                'info': str(e)\n            }\n\n    def post(self):  # 添加数据\n        \"\"\"\n        添加操作\n        :return: json\n        \"\"\"\n        try:\n            model_name = request.json.get('model_name')\n            model_type = request.json.get('model_type')\n            model_dataset = request.json.get('model_dataset')\n\n            wmodel: WModel = WModel()\n            wmodel.model_name = model_name\n            wmodel.model_type = model_type\n            wmodel.model_dataset = model_dataset\n\n            new_path = weights_path + model_name + '.pt'\n            shutil.copy(src=cache_save_model_path, dst=new_path)\n\n            db.session.add(wmodel)\n            db.session.commit()\n\n            return {\n                'status': 'success',\n                'message': '数据添加成功'\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据添加出现异常',\n                'info': str(e)\n            }\n\n\nclass ResultApi(MethodView):\n    \"\"\"\n    后端接口部分 RESTful API风格\n    \"\"\"\n\n    def __init__(self):\n        super(ResultApi, self).__init__()\n\n    def get(self, rid):\n        \"\"\"\n        查询数据\n        :param rid: 结果rid\n        :return: json\n        \"\"\"\n        try:\n            if not rid:\n                n_results: [Result] = Result.query.all()\n                results = [\n                    {\n                        'rid': result.rid,\n                        'wid': result.wid,  # 首要默认为检测模型\n                        'timestamp': result.timestamp,  # 时间戳\n                        'type': result.type,  # 文件类型\n                        'addition': result.addition,  # 额外说明\n                        'mission_type': result.mission_type,  # 任务类型\n                        'filename': result.filename,  # 被检测的源头文件\n                        'num': result.num  # 一共有多少个检测物体\n                    }\n                    for result in n_results\n                ]\n\n            else:\n                result: Result = Result.query.get(rid)\n                results = {\n                    'rid': result.rid,\n                    'wid': result.wid,  # 同理\n                    'timestamp': result.timestamp,\n                    'type': result.type,\n                    'addition': result.addition,\n                    'mission_type': result.mission_type,\n                    'filename': result.filename,\n                    'num': result.num\n                }\n\n            return {\n                'status': 'success',\n                'message': '数据查询成功',\n                'results': results\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据查询出现异常',\n                'info': str(e)\n            }\n\n    def delete(self, rid):\n        \"\"\"\n        删除操作，前面是与 @app.route('/delete/result', methods=['DELETE']) 紧接着操作的，\n        其对应的request.json格式是\n            {\n                \"addition\": \"单独的检测任务\",\n                \"filename\": \"b7a8e795-ff80fddf\",\n                \"mission_type\": \"detection\",\n                \"num\": 23,\n                \"rid\": 1,\n                \"timestamp\": 1677256499,\n                \"type\": \"jpg\",\n                \"wid\": 1\n            }\n        前端从@app.route('/delete/result', methods=['DELETE'])相应完了之后将json的rid传入此函数中\n        :param rid: 结果id\n        :return: json\n        \"\"\"\n        try:\n            result: Result = Result.query.get(rid)\n            path = result_path + result.filename \\\n                   + '_' + str(result.timestamp) + '.' + result.type\n            os.remove(path)\n\n            db.session.delete(result)\n            db.session.commit()\n\n            return {\n                'status': 'success',\n                'message': '数据删除成功',\n            }\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据删除出现异常',\n                'info': str(e)\n            }\n\n    def put(self, rid):\n        try:\n            result: Result = Result.query.get(rid)\n            result.addition = request.json.get('addition')\n            result.mission_type = request.json.get('mission_type')\n\n            db.session.commit()\n\n            return {\n                'status': 'success',\n                'message': '数据修改成功',\n            }\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据修改出现异常',\n                'info': str(e)\n            }\n\n\n\n    def post(self):  # 添加数据\n        \"\"\"\n        添加操作, json来自 @app.route('/detect', methods=['POST']) 那里\n        example: {\n          \"message\": \"检测成功\",\n           传入的json为\n               {\n                  \"addition\": \"单独的检测任务\",\n                  \"attr\": \"jpg\",\n                  \"filename\": \"b7a8e795-ff80fddf\",\n                  \"mission_type\": \"detection\",\n                  \"num\": 21,\n                  \"timestamp\": 1677250829,\n                  \"wid\": 2\n               }\n        }\n\n        :return: json\n        \"\"\"\n        try:\n            form = request.json\n            # print(form)\n\n            result = Result()\n            result.wid = form.get('wid')\n            result.num = form.get('num')\n            result.type = form.get('type')\n            result.timestamp = form.get('timestamp')\n            result.addition = form.get('addition')\n            result.mission_type = form.get('mission_type')\n            result.filename = form.get('filename')\n\n            db.session.add(result)\n            db.session.commit()\n\n            return {\n                'status': 'success',\n                'message': '数据添加成功'\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据添加出现异常',\n                'info': str(e)\n            }\n\n\nclass ObjApi(MethodView):\n    \"\"\"\n    后端接口部分 RESTful API风格\n    \"\"\"\n\n    def __init__(self):\n        super(ObjApi, self).__init__()\n\n    def get(self, rid):\n        \"\"\"\n        查询数据\n        :param rid: 结果rid，外键查询\n        :return: json\n        \"\"\"\n        try:\n            objects: [Object] = db.session.query(Object).filter(Object.rid == rid).all()  # 根据外键rid来查询\n            results = [\n                {\n                    'oid': object.oid,\n                    'rid': object.rid,\n                    'cls': object.cls,\n                    'x1': object.x1,\n                    'y1': object.y1,\n                    'x2': object.x2,\n                    'y2': object.y2,\n                    'conf': object.conf\n                }\n                for object in objects\n            ]\n\n            return {\n                'status': 'success',\n                'message': '数据查询成功',\n                'length': len(results),\n                'results': results\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据查询出现异常',\n                'info': str(e)\n            }\n\n    def delete(self, rid):\n        \"\"\"\n        删除操作，首先执行此函数，\n        然后再执行@app.route('/delete/result', methods=['DELETE'])删除目标文件操作\n        最后再将对应的result实例变量从数据库删除掉\n        :param rid: 结果id\n        :return: json\n        \"\"\"\n        try:\n            objects: [Object] = db.session.query(Object).filter(Object.rid == rid).all()  # 根据外键rid来查询\n            for object in objects:\n                db.session.delete(object)\n            db.session.commit()\n\n            return {\n                'status': 'success',\n                'message': '数据删除成功',\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据删除出现异常',\n                'info': str(e)\n            }\n\n    def post(self):  # 添加数据\n        \"\"\"\n        添加操作, json来自 @app.route('/detect', methods=['POST']) 那里\n        example: {\n          \"message\": \"检测成功\",\n          \"results\": {\n            \"objects\": [\n                    .....\n            ],\n            \"log\": {\n              \"addition\": \"单独的检测任务\",\n              \"attr\": \"jpg\",\n              \"filename\": \"b7a8e795-ff80fddf\",\n              \"mission_type\": \"detection\",\n              \"num\": 21,\n              \"timestamp\": 1677250829,\n              \"wid\": 2\n              }\n            },\n          \"status\": \"success\"\n        }\n\n        :return: json\n        \"\"\"\n        try:\n            objects = request.json['results'][\"objects\"]\n            _about = request.json['results']['log']\n\n            result: Result = db.session.query(Result).filter(Result.timestamp == _about[\"timestamp\"]) \\\n                .filter(Result.filename == _about['filename']).first()\n\n            for obj in objects:\n                object = Object()\n\n                object.rid = result.rid\n                object.cls = obj['cls']\n                object.x1 = obj['x1']\n                object.y1 = obj['y1']\n                object.x2 = obj['x2']\n                object.y2 = obj['y2']\n                object.conf = obj['conf']\n\n                db.session.add(object)\n\n            db.session.commit()\n\n            return {\n                'status': 'success',\n                'message': '数据添加成功'\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数据添加出现异常',\n                'info': str(e)\n            }\n\n\nclass PageApi(MethodView):  # 分页接口\n    def __init__(self):\n        super(PageApi, self).__init__()\n\n    def get(self):\n        try:\n            # print(request.args)\n            curPage = int(request.args.get('curPage'))\n            pageSize = int(request.args.get('pageSize'))\n            tableName = request.args.get('tableName')\n            # print(curPage, pageSize, tableName)\n            results = []\n            if tableName == 'file':\n                resultsIteam = File.query.order_by(File.fid.asc()).paginate(\n                    page=curPage, per_page=pageSize, error_out=True\n                ).items\n                results = [\n                    {\n                        'fid': file.fid,\n                        'name': file.name,\n                        'timestamp': file.timestamp,\n                        'type': file.type,\n                        'origin': file.origin,\n                        'width': file.width,\n                        'height': file.height,\n                        'img_url': img_url_show + str(file.name) + '.'\n                                   + str(file.type)\n                    }\n                    for file in resultsIteam\n                ]\n\n            elif tableName == 'model':\n                resultsIteam = WModel.query.order_by(WModel.wid.asc()).paginate(\n                    page=curPage, per_page=pageSize, error_out=True\n                ).items\n                results = [\n                    {\n                        'wid': wmodel.wid,\n                        'model_name': wmodel.model_name,\n                        'model_type': wmodel.model_type,\n                        'model_dataset': wmodel.model_dataset,\n                    }\n                    for wmodel in resultsIteam\n                ]\n\n            elif tableName == 'result':\n                resultsIteam = Result.query.order_by(Result.rid.asc()).paginate(\n                    page=curPage, per_page=pageSize, error_out=True\n                ).items\n                results = [\n                    {\n                        'rid': result.rid,\n                        'wid': result.wid,\n                        'timestamp': result.timestamp,\n                        'type': result.type,\n                        'addition': result.addition,\n                        'mission_type': result.mission_type,\n                        'filename': result.filename,\n                        'num': result.num,\n                        'img_url': result_url_show + str(result.filename) + '_' + str(result.timestamp) + '.'\n                                   + str(result.type)\n                    }\n                    for result in resultsIteam\n                ]\n\n            else:\n                pass\n\n            return {\n                'status': 'success',\n                'message': '分页查找成功',\n                'results': results\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '分页查找出现异常',\n                'info': str(e)\n            }\n\n\nclass ObjectPageApi(MethodView):\n    def __init__(self):\n        super(ObjectPageApi, self).__init__()\n\n    def get(self, rid):\n        \"\"\"\n        :param rid:\n        Object检测子结果的分页实现\n        :return:\n        \"\"\"\n        try:\n            curPage = int(request.args.get('curPage'))\n            pageSize = int(request.args.get('pageSize'))\n            # print(curPage, pageSize)\n            resultsItem = Object.query \\\n                .filter(Object.rid == rid).order_by(Object.conf.desc()).paginate(\n                page=curPage, per_page=pageSize, error_out=True\n            ).items\n            # print(resultsItem)\n            results = [\n                {\n                    'oid': object.oid,\n                    'rid': object.rid,\n                    'cls': object.cls,\n                    'x1': object.x1,\n                    'y1': object.y1,\n                    'x2': object.x2,\n                    'y2': object.y2,\n                    'conf': object.conf\n                } for object in resultsItem\n            ]\n\n            return {\n                'status': 'success',\n                'message': '分页查找成功',\n                'results': results\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '分页查找出现异常',\n                'info': str(e)\n            }\n\n\nclass NumApi(MethodView):  # 分页显示数目的接口\n    def __init__(self):\n        super(NumApi, self).__init__()\n        self.config = ['file', 'model', 'result']\n\n    def get(self):\n        \"\"\"\n        :param tableName:\n        :return: 总数数字\n        \"\"\"\n        try:\n            results = 0\n            # print(results)\n            # curPage = request.args.get('curPage')\n            # pageSize = request.args.get('pageSize')\n            # print(request.args)\n            tableName = request.args.get('tableName')\n            rid = int(request.args.get('rid'))\n\n            if tableName in self.config:\n                if tableName == 'file':\n                    results = File.query.count()\n                elif tableName == 'model':\n                    results = WModel.query.count()\n                else:\n                    results = Result.query.count()\n            else:\n                if rid > 0:\n                    results = Object.query.filter(Object.rid == rid).count()\n\n            return {\n                'status': 'success',\n                'message': '数量查找成功',\n                'results': results\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数量查找出现异常',\n                'info': str(e)\n            }\n\n\nclass DeblurSearchApi(MethodView):  # 分页实现模糊查找的接口\n    def __init__(self):\n        super(DeblurSearchApi, self).__init__()\n\n    def get(self):\n        try:\n            results = []\n            tableName = request.args.get('tableName')\n            keyword = request.args.get('keyword')\n            curPage = int(request.args.get('curPage'))\n            pageSize = int(request.args.get('pageSize'))\n            # print(tableName, keyword, curPage, pageSize)\n            if tableName == 'file':\n                resultsIteam = File.query.filter(File.name.like('%' + keyword + '%')) \\\n                    .order_by(File.fid.desc()).paginate(\n                    page=curPage, per_page=pageSize, error_out=True\n                ).items\n                # print(len(resultsIteam))\n                results = [\n                    {\n                        'fid': file.fid,\n                        'name': file.name,\n                        'timestamp': file.timestamp,\n                        'type': file.type,\n                        'origin': file.origin,\n                        'width': file.width,\n                        'height': file.height,\n                        'img_url': img_url_show + str(file.name) + '.'\n                                   + str(file.type)\n                    }\n                    for file in resultsIteam\n                ]\n\n            elif tableName == 'model':\n                resultsIteam = WModel.query.filter(WModel.model_name.like('%' + keyword + '%')) \\\n                    .order_by(WModel.wid.asc()).paginate(\n                    page=curPage, per_page=pageSize, error_out=True\n                ).items\n                results = [\n                    {\n                        'wid': wmodel.wid,\n                        'model_name': wmodel.model_name,\n                        'model_type': wmodel.model_type,\n                        'model_dataset': wmodel.model_dataset,\n                    }\n                    for wmodel in resultsIteam\n                ]\n\n            elif tableName == 'result':\n                resultsIteam = Result.query.filter(Result.filename.like('%' + keyword + '%')) \\\n                    .order_by(Result.rid.desc()).paginate(\n                    page=curPage, per_page=pageSize, error_out=True\n                ).items\n\n                results = [\n                    {\n                        'rid': result.rid,\n                        'wid': result.wid,\n                        'timestamp': result.timestamp,\n                        'type': result.type,\n                        'addition': result.addition,\n                        'mission_type': result.mission_type,\n                        'filename': result.filename,\n                        'num': result.num,\n                        'img_url': result_url_show + str(result.filename) + '_' +\n                                   str(result.timestamp) + '.' + str(result.type)\n                    }\n                    for result in resultsIteam\n                ]\n\n            else:\n                pass\n\n            return {\n                'status': 'success',\n                'message': '模糊查找成功',\n                'results': results\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '模糊查找出现异常',\n                'info': str(e)\n            }\n\n\nclass DeblurSearchNumApi(MethodView):\n    def __init__(self):\n        super(DeblurSearchNumApi, self).__init__()\n        self.config = ['file', 'model', 'result']\n\n    def get(self):\n        try:\n            tableName = request.args.get('tableName')\n            # rid = int(request.args.get('rid'))\n            keyword = request.args.get('keyword')\n            results = 0\n\n            if tableName in self.config:\n                if tableName == 'file':\n                    results = File.query.filter(File.name\n                                                .like('%' + keyword + '%')).count()\n                elif tableName == 'model':\n                    results = WModel.query.filter(WModel.model_name\n                                                  .like('%' + keyword + '%')).count()\n                else:\n                    results = Result.query.filter(Result.filename\n                                                  .like('%' + keyword + '%')).count()\n            else:\n                pass\n\n            return {\n                'status': 'success',\n                'message': '数量关键词模糊查找成功',\n                'results': results\n            }\n\n        except Exception as e:\n            return {\n                'status': 'error',\n                'message': '数量关键词模糊查找出现异常',\n                'info': str(e)\n            }\n"
  },
  {
    "path": "detection-backend/app.py",
    "content": "import datetime\nimport random\nimport time\nfrom flask import Flask, request, make_response\nfrom ultralytics import YOLO\nimport cv2\n\nfrom config import save_path, \\\n    result_path, default_weights, default_model_json, weights_path, cache_save_model_path\n\nfrom api import FileApi, ResultApi, ObjApi, WModelApi\nfrom api import PageApi, ObjectPageApi, NumApi\nfrom api import DeblurSearchApi, DeblurSearchNumApi\n\nfrom models import File, WModel\nfrom extension import db, cors\n\nimport os\nimport io\nimport sys\nfrom PIL import Image\nimport requests\n\n# 项目系统路径配置\ncurPath = os.path.dirname(os.path.abspath(__file__))\nroot_path = os.path.abspath(os.path.dirname(curPath) + os.path.sep + os.path.sep)\nsys.path.append(root_path)\n\n# 创建flask项目\napp = Flask(__name__)\n\n# flask项目常量初始化设置\napp.config[\"SQLALCHEMY_DATABASE_URI\"] = 'sqlite:///detection.sqlite'\napp.config[\"SQLALCHEMY_TRACK_MODIFICATIONS\"] = False\napp.config[\"model\"] = YOLO(default_weights)\napp.config[\"model_json\"] = default_model_json\napp.config[\"cls\"] = app.config[\"model\"].names\n\n# 爬虫伪造头\nheaders = {\n    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '\n                  'Chrome/95.0.4638.54 Safari/537.36 Edg/95.0.1020.30 '\n}\n\ndb.init_app(app)  # 注册实例化\ncors.init_app(app)  # 注册跨域请求伪造相关\n\n\"\"\"\n对数据库的各个表相关操作添加相应路由规则\n\"\"\"\n\nfile_view = FileApi.as_view('file_api')\napp.add_url_rule('/files/', defaults={'fid': None},\n                 view_func=file_view, methods=['GET'])  # 查询\napp.add_url_rule('/files', view_func=file_view, methods=['POST'])  # 添加\napp.add_url_rule('/files/<int:fid>', view_func=file_view,\n                 methods=['GET', 'DELETE'])  # 查询，删除\n\nwmodel_view = WModelApi.as_view('wmodel_api')\napp.add_url_rule('/wmodel/', defaults={'wid': None},\n                 view_func=wmodel_view, methods=['GET'])  # 查询\napp.add_url_rule('/wmodel', view_func=wmodel_view, methods=['POST'])  # 添加\napp.add_url_rule('/wmodel/<int:wid>', view_func=wmodel_view,\n                 methods=['GET', 'PUT', 'DELETE'])  # 查询，修改，删除\n\nresult_view = ResultApi.as_view('result_api')\napp.add_url_rule('/result/', defaults={'rid': None},\n                 view_func=result_view, methods=['GET'])  # 查询\napp.add_url_rule('/result', view_func=result_view, methods=['POST'])  # 添加\napp.add_url_rule('/result/<int:rid>', view_func=result_view,\n                 methods=['GET', 'DELETE', 'PUT'])  # 查询，删除，修改\n\nobj_view = ObjApi.as_view('obj_api')\napp.add_url_rule('/obj/', defaults={'rid': 1},\n                 view_func=obj_view, methods=['GET'])  # 查询\napp.add_url_rule('/obj', view_func=obj_view, methods=['POST'])  # 添加\napp.add_url_rule('/obj/<int:rid>', view_func=obj_view,\n                 methods=['GET', 'DELETE'])  # 查询，删除\n\n\"\"\"\n对数据库的各个表相关分页查找实现的路由规则\n各个表相关总数输出实现路由规则\n\"\"\"\n\npage_view = PageApi.as_view('page_api')\napp.add_url_rule('/page', view_func=page_view, methods=['GET'])\n\nobject_page_view = ObjectPageApi.as_view('objectPage_api')\napp.add_url_rule('/objectPage/<int:rid>', view_func=object_page_view, methods=['GET'])\n\nnum_view = NumApi.as_view('num_api')\napp.add_url_rule('/num', view_func=num_view, methods=['GET'])\n\n\"\"\"\n对数据库的各个表相关关键词实现模糊查找\n各个表模糊查找相关总数输出实现路由规则\n\"\"\"\ndeblur_view = DeblurSearchApi.as_view('deblurSearch_api')\napp.add_url_rule('/deblurS', view_func=deblur_view, methods=['GET'])\n\ndeblur_view_num = DeblurSearchNumApi.as_view('deblurSearchNum_api')\napp.add_url_rule('/deblurSNum', view_func=deblur_view_num, methods=['GET'])\n\n\"\"\"\n涉及到对数据库(相关消息传递)之外的功能\n比如 显示图片，爬取图片，切换模型，图片检测，文件上传，文件删除等等\n\"\"\"\n\n# @app.route('/')\n# def hello_world():  # put application's code here\n#     return 'Hello World!'\n\n\"\"\"\n显示图片，上传图片相关操作\n\"\"\"\n\n\n@app.route('/img/<string:name>')\ndef show_img(name):\n    img_url = save_path + name\n    if request.method == 'GET':\n        if name is None:\n            pass\n        else:\n            image_data = open(img_url, \"rb\").read()\n            response = make_response(image_data)\n            response.headers['Content-Type'] = 'image/jpg'\n            return response\n    else:\n        pass\n\n\n@app.route('/upload/pic/', methods=['POST'])\ndef upload_load():\n    \"\"\"\n    从本地上上传图片到后端服务器上\n    :return: json\n    \"\"\"\n    try:\n        filename = request.files.get('file').filename\n        name = filename.split(\".\")[0] + \"_\" + str(int(time.time()))\n        type = filename.split('.')[-1]\n        origin = 'localhost://' + filename\n\n        data = request.files.get('file')\n\n        new_path = save_path + name + '.' + type\n        data.save(new_path, buffer_size=10000000000)\n\n        img = Image.open(new_path)  # 为了获取图片的长宽信息\n\n        width, height = img.width, img.height\n        return {\n            'status': 'success',\n            'message': '图片上传成功',\n            'code': 200,\n            'results': {\n                'name': name,\n                'type': type,\n                'origin': origin,\n                'width': width,\n                'height': height,\n            }\n        }\n\n    except Exception as e:\n        return {\n            'status': 'error',\n            'message': '图片上传出现异常',\n            'info': str(e)\n        }\n\n\n@app.route('/upload/url', methods=['POST'])\ndef get_url_pic():\n    \"\"\"\n    获取url上的图片\n    :param url: 图片的url\n    :return: json\n    \"\"\"\n    try:\n        url = request.json.get(\"url\")\n        res = requests.get(url, headers=headers)\n        if res.status_code == 200:\n            content = res.content\n            file = url.split('/')[-1]\n            name = file.split('.')[0] + '_' + str(int(time.time()))\n            type = file.split('.')[-1]\n            filePath = save_path + name + '.' + type\n\n            # type = file.split('.')[-1]\n            # name = file.split('.')[0]\n\n            origin = url\n\n            with open(filePath, 'wb') as f:\n                f.write(content)\n            f.close()\n\n            img = Image.open(filePath)\n            width, height = img.width, img.height\n\n            return {\n                'status': 'success',\n                'message': '图片爬取成功',\n                'code': 200,\n                'results': {\n                    'name': name,\n                    'type': type,\n                    'origin': origin,\n                    'width': width,\n                    'height': height,\n                }\n            }\n        else:\n            return {\n                'status': 'fail',\n                'message': '图片爬取失败',\n                'code': res.status_code\n            }\n\n    except Exception as e:\n        return {\n            'status': 'error',\n            'message': '图片爬取出现异常',\n            'info': str(e)\n        }\n\n\n\"\"\"\n模型相关操作，切换模型，显示某个模型细节，显示当前模型\n\"\"\"\n\n\n@app.route('/detail/model', methods=['GET'])\ndef show_model_detail():\n    \"\"\"\n    查看某个模型的内部细节\n    :param wid:\n    :return:\n    \"\"\"\n    try:\n        args = request.args\n        # print(model_json)\n        path = weights_path + args.get('model_name') + '.pt'\n\n        _cache_yolo: YOLO = YOLO(path)\n        results = {\n            'names': _cache_yolo.names,\n            'params': _cache_yolo.model.yaml\n        }\n        return {\n            'status': 'success',\n            'message': '查看模型细节成功',\n            'results': results\n        }\n    except Exception as e:\n        return {\n            'status': 'error',\n            'message': '查看模型细节失败',\n            'info': str(e)\n        }\n\n\n@app.route('/current', methods=['GET'])\ndef show_current_model():\n    \"\"\"\n    查看当前模型函数\n    :return: json\n    \"\"\"\n    try:\n        return {\n            'status': 'success',\n            'message': '查看当前模型成功',\n            'results': app.config[\"model_json\"]\n        }\n\n    except Exception as e:\n        return {\n            'status': 'error',\n            'message': '查看当前模型失败',\n            'info': str(e)\n        }\n\n\n@app.route('/switch', methods=[\"PUT\"])\ndef switch_model():\n    \"\"\"\n    前端找wmodel类的参数 -> 获得参数 -> 切换模型\n    切换模型，首先参数要从前端request哪里获得\n    request.json格式是\n    {\n      \"model_dataset\": \"bdd\",\n      \"model_name\": \"yolov8l_bdd\",\n      \"model_type\": \"yolov8\",\n      \"wid\": 1\n    }\n\n    :return: json\n    \"\"\"\n    try:\n        model_json = request.json\n        name = model_json['model_name']\n\n        if name is None:\n            return {\n                'status': 'error',\n                'message': '切换模型失败',\n                'info': '模型名字为空'\n            }\n\n        path = weights_path + model_json['model_name'] + '.pt'\n\n        app.config[\"model\"]: YOLO = YOLO(path)\n        # print(app.config[\"model\"].model.args)\n\n        # print(app.config['model'])\n        app.config[\"model_json\"] = model_json\n        app.config[\"cls\"] = app.config[\"model\"].names\n\n        return {\n            'status': 'success',\n            'message': '切换模型成功',\n        }\n\n    except Exception as e:\n        return {\n            'status': 'error',\n            'message': '切换模型失败',\n            'info': str(e)\n        }\n\n\n@app.route('/upload/model/', methods=[\"POST\"])\ndef save_model_cache():\n    try:\n        data = request.files.get('file')\n        filename = request.files.get('file').filename\n        type = filename.split('.')[-1]\n\n        if type != 'pt':\n            return {\n            'status': 'error',\n            'message': '传送的不是pt文件',\n            }\n\n        data.save(cache_save_model_path, buffer_size=10000000000)\n        return {\n            'status': 'success',\n            'message': '模型暂缓成功',\n        }\n\n    except Exception as e:\n        return {\n            'status': 'error',\n            'message': '模型暂缓出现异常',\n            'info': str(e)\n        }\n\n\n\"\"\"\n检测图片任务相关\n\"\"\"\n\n\n@app.route('/resultImg/<string:name>')\ndef show_result_img(name):\n    \"\"\"\n    返回结果相关\n    :param name: 结果文件名称\n    :return:\n    \"\"\"\n    img_url = result_path + name\n    if request.method == 'GET':\n        if name is None:\n            pass\n        else:\n            image_data = open(img_url, \"rb\").read()\n            response = make_response(image_data)\n            response.headers['Content-Type'] = 'image/jpg'\n            return response\n    else:\n        pass\n\n\n@app.route('/detect', methods=['POST'])\ndef detection():\n    \"\"\"\n    实现检测的功能\n    返回的json参数 与POST http://127.0.0.1:5000/result\n    以及 POST http://127.0.0.1:5000/obj 配合使用\n\n    前端找File类的参数 ->前端找现存文件 ->  绘制图片 -> 保存文件 -> 获得参数 -> 接下来添加数据库对应记录\n                                         └─> 绘制失败则报错\n    与 POST http://127.0.0.1:5000/result\n    以及 POST http://127.0.0.1:5000/obj  结合使用\n\n    最后前端获得检测结果将结果保存到后端上（result,objection表）\n    request.json格式是\n    {\n    \"fid\": 2,\n    \"height\": 720,\n    \"name\": \"b7a8e795-ff80fddf\",\n    \"origin\": \"localhost://b7a8e795-ff80fddf.jpg\",\n    \"timestamp\": \"Fri, 24 Feb 2023 19:04:44 GMT\",\n    \"type\": \"jpg\",\n    \"width\": 1280\n    }\n\n    :return: json\n    \"\"\"\n    try:\n        detection_json = request.json\n\n        filename = detection_json['name']\n        type = detection_json['type']\n\n        model = app.config[\"model\"]\n        cls = app.config[\"cls\"]\n        wid = app.config[\"model_json\"]['wid']\n        # print(cls)\n\n        path = save_path + filename + '.' + type\n        # print(path)\n        results = model(path)\n\n        length = 0\n        info = []\n        for result in results:\n            boxes = result.boxes.data.cpu().numpy().tolist()\n            length = len(boxes)\n\n            # pic_data = []\n            for j in range(length):\n                pic_data = {\n                    'x1': boxes[j][0],\n                    'y1': boxes[j][1],\n                    'x2': boxes[j][2],\n                    'y2': boxes[j][3],\n                    'conf': boxes[j][-2],\n                    'cls': cls[int(boxes[j][-1])]\n                }\n                info.append(pic_data)\n\n        res_plotted = results[0].plot(show_conf=True, line_width=2, font_size=3)\n        timestamp = int(time.time())\n\n        img_path = result_path + filename + '_' + str(timestamp) + '.' + type\n        # print(res_plotted)\n        cv2.imwrite(img_path, res_plotted)\n\n        return {\n            'status': 'success',\n            'message': '检测成功',\n            'results': {\n                'log': {\n                    'wid': wid,\n                    'addition': '单独的检测任务',\n                    'timestamp': timestamp,\n                    'mission_type': 'detection',\n                    'filename': filename,\n                    'type': type,\n                    'num': length\n                },\n                'objects': info\n            }\n        }\n\n    except Exception as e:\n        return {\n            'status': 'error',\n            'message': '检测失败',\n            'info': str(e)\n        }\n\n\n@app.route('/delete/result', methods=['DELETE'])\ndef delete_result():\n    \"\"\"\n    这里的函数是实现删除结果图片文件，与 DELETE http://127.0.0.1:5000/result/<int:rid>\n                               以及 DELETE http://127.0.0.1:5000/obj/<int:rid>  结合使用\n    前端返回参数 ->前端找现存文件 ->  删除图片 -> 删除完成 -> 接下来删除数据库对应记录\n                                   └─> 删除失败则报错\n    request.json格式是\n    {\n        \"addition\": \"单独的检测任务\",\n        \"filename\": \"b7a8e795-ff80fddf\",\n        \"mission_type\": \"detection\",\n        \"num\": 23,\n        \"rid\": 1,\n        \"timestamp\": 1677256499,\n        \"type\": \"jpg\",\n        \"wid\": 1\n    }\n\n    :return: json\n    \"\"\"\n    try:\n        result_json = request.json\n        file = result_json['filename'] + '_' + str(result_json['timestamp']) + '.' + result_json['type']\n        path = result_path + file\n        os.remove(path)\n\n        return {\n            'status': 'success',\n            'message': '删除记录图片成功',\n        }\n\n    except Exception as e:\n        return {\n            'status': 'error',\n            'message': '删除记录图片失败',\n            'info': str(e)\n        }\n\n\n@app.route('/delete/file', methods=['DELETE'])\ndef delete_file():\n    \"\"\"\n    这里的函数是实现删除结果图片文件，与 DELETE   结合使用\n    前端返回参数 ->前端找现存文件 ->  删除图片 -> 删除完成 -> 接下来删除数据库对应记录\n                                   └─> 删除失败则报错\n\n    request.json格式是,来自前端上面的\n    {\n          type: response.results.type,\n          name: response.results.name,\n    }\n    :return: json\n    \"\"\"\n    try:\n        type = request.json.get(\"type\")\n        name = request.json.get(\"name\")\n\n        file_path = save_path + name + '.' + type\n        # print(file_path)\n        os.remove(file_path)\n        # print(request.data)\n        # print(request.json.get(\"type\"), request.json.get('name'))\n        # print(request.json)\n        return {\n            'status': 'success',\n            'message': '删除图片成功',\n        }\n\n    except Exception as e:\n        return {\n            'status': 'error',\n            'message': '删除图片失败',\n            'info': str(e)\n        }\n    pass\n\n\n# @app.route('/delete/model', methods=['DELETE'])\n# def delete_model():\n#     \"\"\"\n#     这里的函数是实现删除结果图片文件，与 DELETE http://127.0.0.1:5000/result/<int:rid> 结合使用\n#     前端返回参数 ->前端找现存文件 ->  删除图片 -> 删除完成 -> 接下来删除数据库对应记录\n#                                    └─> 删除失败则报错\n#     request.json格式是\n#\n#\n#     :return: json\n#     \"\"\"\n#     pass\n\n\n\"\"\"\n指令相关\n\"\"\"\n\n\n@app.cli.command('init')\ndef init():\n    db.drop_all()  # 注销所有的数据库\n    db.create_all()  # 创建所有数据库\n    File.init_db()\n    WModel.init_db()\n\n\nif __name__ == '__main__':\n    app.run(debug=True)\n"
  },
  {
    "path": "detection-backend/config.py",
    "content": "save_path = '.\\\\pic\\\\save\\\\'\ncache_save_model_path = '.\\\\save\\\\cache.pt'\nresult_path = '.\\\\pic\\\\result\\\\'\nweights_path = '.\\\\weights\\\\'\n\ncache_save_path = '.\\\\cache\\\\pic\\\\'\ncache_model_path = '.\\\\cache\\\\model\\\\'\n\nimg_url_show = 'http://localhost:5000/img/'\nresult_url_show = 'http://localhost:5000/resultImg/'\n\ndefault_weights = '.\\\\weights\\\\yolov8m_coco.pt'\ndefault_model_json = {\n    \"model_dataset\": \"bdd\",\n    \"model_name\": \"yolov8m_bdd\",\n    \"model_type\": \"yolov8\",\n    \"wid\": 2\n}\n\nfixed_model_type = [\n    'yolov5', 'yolov8', 'deepRFT'\n]\n"
  },
  {
    "path": "detection-backend/demo.http",
    "content": "###\nGET http://127.0.0.1:5000/files/2\n\n###\nGET http://127.0.0.1:5000/wmodel\n\n### 下面是从切换模型，检测开始记录数据，查询数据，删除数据的步骤\n###\n###\nPUT http://127.0.0.1:5000/switch\nContent-Type: application/json\n\n    {\n      \"model_dataset\": \"bdd\",\n      \"model_name\": \"yolov8l_bdd\",\n      \"model_type\": \"yolov8\",\n      \"wid\": 1\n    }\n\n###\nGET http://127.0.0.1:5000/files/113\n\n###\nPOST http://127.0.0.1:5000/detect\nContent-Type: application/json\n\n{\n    \"fid\": 113,\n    \"height\": 720,\n    \"name\": \"b7bcc17a-72df68f9\",\n    \"origin\": \"localhost://b7bcc17a-72df68f9.jpg\",\n    \"timestamp\": \"Tue, 28 Feb 2023 09:32:58 GMT\",\n    \"type\": \"jpg\",\n    \"width\": 1280\n}\n\n###\nPOST http://127.0.0.1:5000/result\nContent-Type: application/json\n\n{\n      \"addition\": \"单独的检测任务\",\n      \"filename\": \"b7bcc17a-72df68f9\",\n      \"mission_type\": \"detection\",\n      \"num\": 26,\n      \"timestamp\": 1677658144,\n      \"type\": \"jpg\",\n      \"wid\": 1\n    }\n\n\n\n###\nPOST http://127.0.0.1:5000/obj\nContent-Type: application/json\n\n{\n  \"message\": \"检测成功\",\n  \"results\": {\n    \"log\": {\n      \"addition\": \"单独的检测任务\",\n      \"filename\": \"b7bcc17a-72df68f9\",\n      \"mission_type\": \"detection\",\n      \"num\": 26,\n      \"timestamp\": 1677658144,\n      \"type\": \"jpg\",\n      \"wid\": 1\n    },\n    \"objects\": [\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.9426229596138,\n        \"x1\": 21.0,\n        \"x2\": 335.0,\n        \"y1\": 502.0,\n        \"y2\": 651.0\n      },\n      {\n        \"cls\": \"bus\",\n        \"conf\": 0.9366695284843445,\n        \"x1\": 819.0,\n        \"x2\": 1019.0,\n        \"y1\": 451.0,\n        \"y2\": 611.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.9037463665008545,\n        \"x1\": 588.0,\n        \"x2\": 695.0,\n        \"y1\": 518.0,\n        \"y2\": 608.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.8302534222602844,\n        \"x1\": 757.0,\n        \"x2\": 812.0,\n        \"y1\": 527.0,\n        \"y2\": 567.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.8223326802253723,\n        \"x1\": 363.0,\n        \"x2\": 419.0,\n        \"y1\": 518.0,\n        \"y2\": 555.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.7934474945068359,\n        \"x1\": 414.0,\n        \"x2\": 448.0,\n        \"y1\": 521.0,\n        \"y2\": 550.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.7931928038597107,\n        \"x1\": 690.0,\n        \"x2\": 723.0,\n        \"y1\": 531.0,\n        \"y2\": 560.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.7816309332847595,\n        \"x1\": 445.0,\n        \"x2\": 468.0,\n        \"y1\": 526.0,\n        \"y2\": 548.0\n      },\n      {\n        \"cls\": \"traffic light\",\n        \"conf\": 0.7765074372291565,\n        \"x1\": 548.0,\n        \"x2\": 564.0,\n        \"y1\": 409.0,\n        \"y2\": 452.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.7473191618919373,\n        \"x1\": 545.0,\n        \"x2\": 577.0,\n        \"y1\": 527.0,\n        \"y2\": 552.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.7085943818092346,\n        \"x1\": 18.0,\n        \"x2\": 102.0,\n        \"y1\": 496.0,\n        \"y2\": 548.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.7016028761863708,\n        \"x1\": 723.0,\n        \"x2\": 748.0,\n        \"y1\": 531.0,\n        \"y2\": 553.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.64445960521698,\n        \"x1\": 744.0,\n        \"x2\": 765.0,\n        \"y1\": 532.0,\n        \"y2\": 557.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.6331145763397217,\n        \"x1\": 70.0,\n        \"x2\": 132.0,\n        \"y1\": 507.0,\n        \"y2\": 536.0\n      },\n      {\n        \"cls\": \"traffic light\",\n        \"conf\": 0.6008689999580383,\n        \"x1\": 1094.0,\n        \"x2\": 1119.0,\n        \"y1\": 489.0,\n        \"y2\": 509.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.5753867030143738,\n        \"x1\": 523.0,\n        \"x2\": 539.0,\n        \"y1\": 528.0,\n        \"y2\": 543.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.5730618238449097,\n        \"x1\": 2.0,\n        \"x2\": 75.0,\n        \"y1\": 499.0,\n        \"y2\": 555.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.5328835248947144,\n        \"x1\": 587.0,\n        \"x2\": 604.0,\n        \"y1\": 526.0,\n        \"y2\": 543.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.5161319971084595,\n        \"x1\": 462.0,\n        \"x2\": 494.0,\n        \"y1\": 524.0,\n        \"y2\": 547.0\n      },\n      {\n        \"cls\": \"traffic light\",\n        \"conf\": 0.5084584951400757,\n        \"x1\": 1078.0,\n        \"x2\": 1095.0,\n        \"y1\": 431.0,\n        \"y2\": 471.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.5038861632347107,\n        \"x1\": 302.0,\n        \"x2\": 337.0,\n        \"y1\": 521.0,\n        \"y2\": 564.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.41518062353134155,\n        \"x1\": 682.0,\n        \"x2\": 699.0,\n        \"y1\": 528.0,\n        \"y2\": 547.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.36542969942092896,\n        \"x1\": 573.0,\n        \"x2\": 589.0,\n        \"y1\": 525.0,\n        \"y2\": 539.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.35099053382873535,\n        \"x1\": 307.0,\n        \"x2\": 361.0,\n        \"y1\": 512.0,\n        \"y2\": 543.0\n      },\n      {\n        \"cls\": \"car\",\n        \"conf\": 0.31251752376556396,\n        \"x1\": 504.0,\n        \"x2\": 527.0,\n        \"y1\": 528.0,\n        \"y2\": 545.0\n      },\n      {\n        \"cls\": \"traffic sign\",\n        \"conf\": 0.2699459195137024,\n        \"x1\": 613.0,\n        \"x2\": 634.0,\n        \"y1\": 473.0,\n        \"y2\": 492.0\n      }\n    ]\n  },\n  \"status\": \"success\"\n}\n\n\n###\nGET  http://127.0.0.1:5000/obj/1\n\n###\nDELETE http://127.0.0.1:5000/obj/1\n\n###\nGET http://127.0.0.1:5000/result/\n\n###\nGET http://127.0.0.1:5000/result/1\n\n###\nDELETE http://127.0.0.1:5000/delete/result\nContent-Type: application/json\n\n {\n      \"addition\": \"单独的检测任务\",\n      \"filename\": \"b7a8e795-ff80fddf\",\n      \"mission_type\": \"detection\",\n      \"num\": 23,\n      \"rid\": 1,\n      \"timestamp\": 1677258002,\n      \"type\": \"jpg\",\n      \"wid\": 1\n    }\n\n###\nDELETE http://127.0.0.1:5000/result/1\n\n####分页函数测试\nGET http://127.0.0.1:5000/page/\nContent-Type: application/json\n\n{\n  \"curPage\": 1,\n  \"pageSize\": 2,\n  \"tableName\": \"file\"\n}\n\n###\nGET http://127.0.0.1:5000/objectPage/1\nContent-Type: application/json\n\n{\n    \"curPage\": 3,\n    \"pageSize\": 5\n}\n\n###\nGET http://127.0.0.1:5000/num/\nContent-Type: application/json\n\n{\n  \"rid\": 1,\n  \"tableName\": \"model\"\n}\n\n####模糊查找测试\n\nGET http://127.0.0.1:5000/deblurS/\nContent-Type: application/json\n\n{\n    \"keyword\": \"b\",\n  \"tableName\": \"result\"\n}\n\n\n###查看当前模型参数测试\nGET http://127.0.0.1:5000/current"
  },
  {
    "path": "detection-backend/extension.py",
    "content": "from flask_sqlalchemy import SQLAlchemy\nfrom flask_cors import CORS\n\n\ndb = SQLAlchemy()\ncors = CORS(resources={r'/*':{'origins':'*'}})\n"
  },
  {
    "path": "detection-backend/modelPredict.py",
    "content": "from ultralytics import YOLO\n\nmodel_path = './weights/yolov8n_bdd.pt'\nvideo_path = 'video/save/2.mp4'\n\nmodel = YOLO(model_path)\nresults = model(video_path, show=True, save=True)\n\n"
  },
  {
    "path": "detection-backend/models.py",
    "content": "import os\n\nfrom sqlalchemy import ForeignKey\nfrom config import cache_save_path\nfrom PIL import Image\n\nfrom extension import db\nimport datetime\n\nfixed_model_type = [\n    'yolov5', 'yolov8'\n]\n\n\nclass File(db.Model):\n    __tablename__ = 'file_table'\n    fid = db.Column(db.Integer, primary_key=True, autoincrement=True)\n    name = db.Column(db.String(255), nullable=False)\n    timestamp = db.Column(db.DateTime, default=datetime.datetime.now)\n    type = db.Column(db.String(255), nullable=False)  # 文件类型\n    origin = db.Column(db.String(255), nullable=False)  # 来源\n    width = db.Column(db.Integer, nullable=False)\n    height = db.Column(db.Integer, nullable=False)\n\n    @staticmethod\n    def init_db():\n        count: int = 1\n        files = os.listdir(cache_save_path)\n\n        # rets = [\n        #     (1, 'b6f77fda-02b6e75b', 'jpg', 'localhost://b6f77fda-02b6e75b.jpg', 1280, 720),\n        #     (2, 'b7a8e795-ff80fddf', 'jpg', 'localhost://b7a8e795-ff80fddf.jpg', 1280, 720),\n        # ]\n\n        rets = []\n        for filename in files:\n            filename = cache_save_path + filename\n            # print(filename)\n            img = Image.open(filename)\n            rets.append([\n                count, filename.split('\\\\')[-1].split('.')[0], filename.split('.')[-1],\n                'localhost://' + filename.split('\\\\')[-1], img.width, img.height\n            ])\n            count += 1\n        # print(rets)\n\n        for ret in rets:\n            file = File()\n            file.fid = ret[0]\n            file.name = ret[1]\n            file.type = ret[2]\n            file.origin = ret[3]\n            file.width = ret[4]\n            file.height = ret[5]\n            db.session.add(file)\n        #\n        db.session.commit()\n\n\nclass WModel(db.Model):\n    __tablename__ = 'model_table'\n    wid = db.Column(db.Integer, primary_key=True, autoincrement=True)\n    model_name = db.Column(db.String(255), nullable=False)  # 名称\n    model_type = db.Column(db.String(255), nullable=False)  # 类型\n    model_dataset = db.Column(db.String(255), nullable=False)  # 适用数据集\n\n    @staticmethod\n    def init_db():\n        rets = [\n            (1, 'yolov8l_coco', 'yolov8', 'coco'),\n            (2, 'yolov8m_coco', 'yolov8', 'coco'),\n            (3, 'yolov8s_coco', 'yolov8', 'coco'),\n            (4, 'yolov8n_coco', 'yolov8', 'coco'),\n            (5, 'yolov5su_coco', 'yolov5', 'coco'),\n            (6, 'yolov5nu_coco', 'yolov5', 'coco')\n        ]\n\n        for ret in rets:\n            wmodel = WModel()\n            wmodel.wid = ret[0]\n            wmodel.model_name = ret[1]\n            wmodel.model_type = ret[2]\n            wmodel.model_dataset = ret[3]\n            db.session.add(wmodel)\n\n        db.session.commit()\n\n\nclass Result(db.Model):\n    __tablename__ = 'result_table'\n    rid = db.Column(db.Integer, primary_key=True, autoincrement=True)\n    wid = db.Column(db.Integer, ForeignKey(\"model_table.wid\", ondelete='SET NULL'))  # 首要默认为检测模型\n    timestamp = db.Column(db.Integer, nullable=False)  # 时间戳\n    type = db.Column(db.String(255), nullable=True)  # 文件类型\n    addition = db.Column(db.String(255), nullable=True)  # 额外说明\n    mission_type = db.Column(db.String(255), nullable=False)  # 任务类型\n    filename = db.Column(db.String(255), nullable=False)  # 被检测的源头文件\n    num = db.Column(db.Integer, nullable=False)  # 一共有多少个检测物体\n\n\nclass Object(db.Model):\n    __tablename__ = 'obj_table'\n    oid = db.Column(db.Integer, primary_key=True, autoincrement=True)\n    rid = db.Column(db.Integer, ForeignKey(\"result_table.rid\", ondelete='SET NULL'))  # 和检测完成好的文件相关联, 外键\n    cls = db.Column(db.String(255), nullable=False)  # 类别名称\n    x1 = db.Column(db.Float, nullable=False)  # 边框的坐标值\n    y1 = db.Column(db.Float, nullable=False)\n    x2 = db.Column(db.Float, nullable=False)\n    y2 = db.Column(db.Float, nullable=False)\n    conf = db.Column(db.Float, nullable=False)  # 置信度\n"
  },
  {
    "path": "detection-fontend/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016-2023 vue-manage-system\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."
  },
  {
    "path": "detection-fontend/README.md",
    "content": "# vue-manage-system\n\n<a href=\"https://github.com/vuejs/vue\">\n    <img src=\"https://img.shields.io/badge/vue-3.1.2-brightgreen.svg\" alt=\"vue\">\n  </a>\n  <a href=\"https://github.com/vuejs/pinia\">\n    <img src=\"https://img.shields.io/badge/pinia-2.0.14-brightgreen.svg\" alt=\"pinia\">\n  </a>\n  <a href=\"https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/mashape/apistatus.svg\" alt=\"license\">\n  </a>\n  <a href=\"https://github.com/lin-xin/vue-manage-system/releases\">\n    <img src=\"https://img.shields.io/github/release/lin-xin/vue-manage-system.svg\" alt=\"GitHub release\">\n  </a>\n  <a href=\"https://lin-xin.gitee.io/example/work/#/donate\">\n    <img src=\"https://img.shields.io/badge/%24-donate-ff69b4.svg\" alt=\"donate\">\n  </a>\n\n基于 Vue3 + pinia + Element Plus 的后台管理系统解决方案。[线上地址](https://lin-xin.gitee.io/example/work/)\n\n> Vue2 版本请看 [tag-V4.2.0](https://github.com/lin-xin/vue-manage-system/tree/V4.2.0)\n\n[English document](https://github.com/lin-xin/manage-system/blob/master/README_EN.md)\n\n## 赞助商\n\n### 好问\n\n[<img src=\"https://static.bestqa.net/logo/bestqa_haowen.png\" width=\"220\" height=\"100\">](https://www.bestqa.net/home/index.html)\n\n专业问卷服务，一对一客服，按需定制 \n\n## 支持作者\n\n请作者喝杯咖啡吧！(微信号：linxin_20)\n\n![微信扫一扫](https://lin-xin.gitee.io/images/weixin.jpg)\n\n## 前言\n\n该方案作为一套多功能的后台框架模板，适用于绝大部分的后台管理系统开发。基于 Vue3 + pinia + typescript，引用 Element Plus 组件库，方便开发。实现逻辑简单，适合外包项目，快速交付。\n\n## 功能\n\n-   [x] Element Plus\n-   [x] vite 3\n-   [x] pinia\n-   [x] typescript\n-   [x] 登录/注销\n-   [x] Dashboard\n-   [x] 表格\n-   [x] Tab 选项卡\n-   [x] 表单\n-   [x] 图表 :bar_chart:\n-   [x] 富文本/markdown编辑器\n-   [x] 图片拖拽/裁剪上传\n-   [x] 权限管理\n-   [x] 三级菜单\n-   [x] 自定义图标\n\n\n## 安装步骤\n> 因为使用vite3，node版本需要 14.18+\n\n```\ngit clone https://github.com/lin-xin/vue-manage-system.git      // 把模板下载到本地\ncd vue-manage-system    // 进入模板目录\nnpm install         // 安装项目依赖，等待安装完成之后，安装失败可用 cnpm 或 yarn\n\n// 运行\nnpm run dev\n\n// 执行构建命令，生成的dist文件夹放在服务器下即可访问\nnpm run build\n```\n\n## 组件使用说明与演示\n\n### vue-schart\n\nvue.js 封装 sChart.js 的图表组件。访问地址：[vue-schart](https://github.com/lin-xin/vue-schart#/) \n\n<p><a href=\"https://www.npmjs.com/package/vue-schart\"><img src=\"https://img.shields.io/npm/dm/vue-schart.svg\" alt=\"Downloads\"></a></p>\n\n```html\n<template>\n    <div>\n        <schart class=\"wrapper\" canvasId=\"myCanvas\" :options=\"options\"></schart>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport Schart from \"vue-schart\"; // 导入Schart组件\nconst options = ref({\n    type: \"bar\",\n    title: {\n        text: \"最近一周各品类销售图\",\n    },\n    labels: [\"周一\", \"周二\", \"周三\", \"周四\", \"周五\"],\n    datasets: [\n        {\n            label: \"家电\",\n            data: [234, 278, 270, 190, 230],\n        },\n        {\n            label: \"百货\",\n            data: [164, 178, 190, 135, 160],\n        },\n        {\n            label: \"食品\",\n            data: [144, 198, 150, 235, 120],\n        },\n    ],\n})\n</script>\n<style>\n    .wrapper {\n        width: 7rem;\n        height: 5rem;\n    }\n</style>\n```\n\n## 项目截图\n\n### 登录\n\n![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms3.png)\n\n### 首页\n\n![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms1.png)\n\n## License\n\n[MIT](https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE)\n"
  },
  {
    "path": "detection-fontend/README_EN.md",
    "content": "# vue-manage-system\n\n<a href=\"https://github.com/vuejs/vue\">\n    <img src=\"https://img.shields.io/badge/vue-2.6.10-brightgreen.svg\" alt=\"vue\">\n  </a>\n  <a href=\"https://github.com/ElemeFE/element\">\n    <img src=\"https://img.shields.io/badge/element--ui-2.8.2-brightgreen.svg\" alt=\"element-ui\">\n  </a>\n  <a href=\"https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/mashape/apistatus.svg\" alt=\"license\">\n  </a>\n  <a href=\"https://github.com/lin-xin/vue-manage-system/releases\">\n    <img src=\"https://img.shields.io/github/release/lin-xin/vue-manage-system.svg\" alt=\"GitHub release\">\n  </a>\n  <a href=\"https://lin-xin.gitee.io/example/work/#/donate\">\n    <img src=\"https://img.shields.io/badge/%24-donate-ff69b4.svg\" alt=\"donate\">\n  </a>\n\nThe web management system solution based on Vue3 and ElementPlus。[live demo](https://lin-xin.gitee.io/example/work/)\n\nPlease check the version of vue2 in [tag V4.2.0](https://github.com/lin-xin/vue-manage-system/tree/V4.2.0)\n\n## Donation\n\n![WeChat](https://lin-xin.gitee.io/images/weixin.jpg)\n\n## Preface\n\nThe scheme as a set of multi-function background frame templates, suitable for most of the WEB management system development. Convenient development fast simple good components based on Vue3 and ElementPlus. Color separation of color style, support manual switch themes, and it is convenient to use a custom theme color.\n\n## Function\n\n-   [x] Element-UI\n-   [x] Login/Logout\n-   [x] Dashboard\n-   [x] Table\n-   [x] Tabs\n-   [x] From\n-   [x] Chart :bar_chart:\n-   [x] Editor\n-   [x] Markdown\n-   [x] Upload pictures by clipping or dragging\n-   [x] Permission\n-   [x] Three level menu\n-   [x] Custom icon\n\n## Installation steps\n\n    git clone https://github.com/lin-xin/vue-manage-system.git\t\t// Clone templates\n    cd vue-manage-system\t\t\t\t\t\t\t\t\t\t\t// Enter template directory\n    npm install\t\t\t\t\t\t\t\t\t\t\t\t\t// Installation dependency\n\n## Local development\n\n    npm run dev\n\n## Constructing production\n\n    // Constructing project\n    npm run build\n\n## Component description and presentation\n\n### vue-schart\n\nVue.js wrapper for sChart.js. Github : [vue-schart](https://github.com/lin-xin/vue-schart#/)\n\n```html\n<template>\n    <div>\n        <schart class=\"wrapper\" canvasId=\"myCanvas\" :options=\"options\"></schart>\n    </div>\n</template>\n<script setup>\nimport { ref } from 'vue';\nimport Schart from \"vue-schart\"; // 导入Schart组件\nconst options = ref({\n    type: \"bar\",\n    title: {\n        text: \"最近一周各品类销售图\",\n    },\n    labels: [\"周一\", \"周二\", \"周三\", \"周四\", \"周五\"],\n    datasets: [\n        {\n            label: \"家电\",\n            data: [234, 278, 270, 190, 230],\n        },\n        {\n            label: \"百货\",\n            data: [164, 178, 190, 135, 160],\n        },\n        {\n            label: \"食品\",\n            data: [144, 198, 150, 235, 120],\n        },\n    ],\n})\n</script>\n<style>\n    .wrapper {\n        width: 7rem;\n        height: 5rem;\n    }\n</style>\n```\n\n## Screenshot\n\n### Default theme\n\n![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms1.png)\n\n### Login\n\n![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms3.png)\n\n## License\n\n[MIT](https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE)\n"
  },
  {
    "path": "detection-fontend/auto-imports.d.ts",
    "content": "// Generated by 'unplugin-auto-import'\nexport {}\ndeclare global {\n\n}\n"
  },
  {
    "path": "detection-fontend/components.d.ts",
    "content": "// generated by unplugin-vue-components\n// We suggest you to commit this file into source control\n// Read more: https://github.com/vuejs/core/pull/3399\nimport '@vue/runtime-core'\n\nexport {}\n\ndeclare module '@vue/runtime-core' {\n  export interface GlobalComponents {\n    ElAvatar: typeof import('element-plus/es')['ElAvatar']\n    ElButton: typeof import('element-plus/es')['ElButton']\n    ElCard: typeof import('element-plus/es')['ElCard']\n    ElCascader: typeof import('element-plus/es')['ElCascader']\n    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']\n    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']\n    ElCol: typeof import('element-plus/es')['ElCol']\n    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']\n    ElDescriptions: typeof import('element-plus/es')['ElDescriptions']\n    ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']\n    ElDialog: typeof import('element-plus/es')['ElDialog']\n    ElDropdown: typeof import('element-plus/es')['ElDropdown']\n    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']\n    ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']\n    ElForm: typeof import('element-plus/es')['ElForm']\n    ElFormItem: typeof import('element-plus/es')['ElFormItem']\n    ElIcon: typeof import('element-plus/es')['ElIcon']\n    ElImage: typeof import('element-plus/es')['ElImage']\n    ElInput: typeof import('element-plus/es')['ElInput']\n    ElLink: typeof import('element-plus/es')['ElLink']\n    ElMenu: typeof import('element-plus/es')['ElMenu']\n    ElMenuItem: typeof import('element-plus/es')['ElMenuItem']\n    ElOption: typeof import('element-plus/es')['ElOption']\n    ElPagination: typeof import('element-plus/es')['ElPagination']\n    ElProgress: typeof import('element-plus/es')['ElProgress']\n    ElRadio: typeof import('element-plus/es')['ElRadio']\n    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']\n    ElRow: typeof import('element-plus/es')['ElRow']\n    ElSelect: typeof import('element-plus/es')['ElSelect']\n    ElSubMenu: typeof import('element-plus/es')['ElSubMenu']\n    ElSwitch: typeof import('element-plus/es')['ElSwitch']\n    ElTable: typeof import('element-plus/es')['ElTable']\n    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']\n    ElTabPane: typeof import('element-plus/es')['ElTabPane']\n    ElTabs: typeof import('element-plus/es')['ElTabs']\n    ElTag: typeof import('element-plus/es')['ElTag']\n    ElTimePicker: typeof import('element-plus/es')['ElTimePicker']\n    ElTooltip: typeof import('element-plus/es')['ElTooltip']\n    ElUpload: typeof import('element-plus/es')['ElUpload']\n    Header: typeof import('./src/components/header.vue')['default']\n    RouterLink: typeof import('vue-router')['RouterLink']\n    RouterView: typeof import('vue-router')['RouterView']\n    Sidebar: typeof import('./src/components/sidebar.vue')['default']\n    Tags: typeof import('./src/components/tags.vue')['default']\n  }\n}\n"
  },
  {
    "path": "detection-fontend/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n  <title>vue-manage-system</title>\n  <link rel=\"stylesheet\" href=\"https://at.alicdn.com/t/font_830376_qzecyukz0s.css\">\n</head>\n\n<body>\n  <noscript>\n    <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.\n      Please enable it to continue.</strong>\n  </noscript>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"/src/main.ts\"></script>\n  <!-- built files will be auto injected -->\n</body>\n\n</html>"
  },
  {
    "path": "detection-fontend/package.json",
    "content": "{\n\t\"name\": \"vue-manage-system\",\n\t\"version\": \"5.3.0\",\n\t\"private\": true,\n\t\"scripts\": {\n\t\t\"dev\": \"vite\",\n\t\t\"build\": \"vue-tsc --noEmit && vite build\",\n\t\t\"serve\": \"vite preview\"\n\t},\n\t\"dependencies\": {\n\t\t\"@element-plus/icons-vue\": \"^2.0.9\",\n\t\t\"axios\": \"^0.27.2\",\n\t\t\"element-plus\": \"^2.2.14\",\n\t\t\"md-editor-v3\": \"^2.2.1\",\n\t\t\"pinia\": \"^2.0.20\",\n\t\t\"vue\": \"^3.2.37\",\n\t\t\"vue-cropperjs\": \"^5.0.0\",\n\t\t\"vue-router\": \"^4.1.3\",\n\t\t\"vue-schart\": \"^2.0.0\",\n\t\t\"wangeditor\": \"^4.7.15\",\n\t\t\"xlsx\": \"^0.18.5\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@vitejs/plugin-vue\": \"^3.0.0\",\n\t\t\"@vue/compiler-sfc\": \"^3.1.2\",\n\t\t\"typescript\": \"^4.6.4\",\n\t\t\"unplugin-auto-import\": \"^0.11.2\",\n\t\t\"unplugin-vue-components\": \"^0.22.4\",\n\t\t\"vite\": \"^3.0.0\",\n\t\t\"vite-plugin-vue-setup-extend\": \"^0.4.0\",\n\t\t\"vue-tsc\": \"^0.38.4\"\n\t},\n\t\"browserslist\": [\n\t\t\"> 1%\",\n\t\t\"last 2 versions\",\n\t\t\"not dead\"\n\t]\n}\n"
  },
  {
    "path": "detection-fontend/public/table.json",
    "content": "{\n    \"list\": [{\n            \"id\": 1,\n            \"name\": \"张三\",\n            \"money\": 123,\n            \"address\": \"广东省东莞市长安镇\",\n            \"state\": \"成功\",\n            \"date\": \"2019-11-1\",\n            \"thumb\": \"https://lin-xin.gitee.io/images/post/wms.png\"\n        },\n        {\n            \"id\": 2,\n            \"name\": \"李四\",\n            \"money\": 456,\n            \"address\": \"广东省广州市白云区\",\n            \"state\": \"成功\",\n            \"date\": \"2019-10-11\",\n            \"thumb\": \"https://lin-xin.gitee.io/images/post/node3.png\"\n        },\n        {\n            \"id\": 3,\n            \"name\": \"王五\",\n            \"money\": 789,\n            \"address\": \"湖南省长沙市\",\n            \"state\": \"失败\",\n            \"date\": \"2019-11-11\",\n            \"thumb\": \"https://lin-xin.gitee.io/images/post/parcel.png\"\n        },\n        {\n            \"id\": 4,\n            \"name\": \"赵六\",\n            \"money\": 1011,\n            \"address\": \"福建省厦门市鼓浪屿\",\n            \"state\": \"成功\",\n            \"date\": \"2019-10-20\",\n            \"thumb\": \"https://lin-xin.gitee.io/images/post/notice.png\"\n        }\n    ],\n    \"pageTotal\": 4\n}"
  },
  {
    "path": "detection-fontend/src/App.vue",
    "content": "<template>\n    <el-config-provider :locale=\"zhCn\">\n        <router-view />\n    </el-config-provider>\n</template>\n\n<script setup lang=\"ts\">\nimport { ElConfigProvider } from 'element-plus';\nimport zhCn from 'element-plus/es/locale/lang/zh-cn';\n</script>\n<style>\n@import './assets/css/main.css';\n@import './assets/css/color-dark.css';\n</style>\n"
  },
  {
    "path": "detection-fontend/src/api/index.ts",
    "content": "import request from '../utils/request';\n\nexport const fetchData = () => {\n    return request({\n        url: './table.json',\n        method: 'get'\n    });\n};\n"
  },
  {
    "path": "detection-fontend/src/assets/css/color-dark.css",
    "content": ".header{\n    background-color: #242f42;\n}\n.login-wrap{\n    background: #324157;\n}\n.plugins-tips{\n    background: #eef1f6;\n}\n.plugins-tips a{\n    color: #20a0ff;\n}\n\n.tags-li.active {\n    border: 1px solid #409EFF;\n    background-color: #409EFF;\n}\n.message-title{\n    color: #20a0ff;\n}\n.collapse-btn:hover{\n    background: rgb(40,52,70);\n}"
  },
  {
    "path": "detection-fontend/src/assets/css/icon.css",
    "content": "[class*=\" el-icon-lx\"],\n[class^=el-icon-lx] {\n    font-family: lx-iconfont !important;\n}"
  },
  {
    "path": "detection-fontend/src/assets/css/main.css",
    "content": "* {\n    margin: 0;\n    padding: 0;\n}\n\nhtml,\nbody,\n#app,\n.wrapper {\n    width: 100%;\n    height: 100%;\n    overflow: hidden;\n}\n\nbody {\n    font-family: 'PingFang SC', \"Helvetica Neue\", Helvetica, \"microsoft yahei\", arial, STHeiTi, sans-serif;\n}\n\na {\n    text-decoration: none\n}\n\n\n.content-box {\n    position: absolute;\n    left: 250px;\n    right: 0;\n    top: 70px;\n    bottom: 0;\n    padding-bottom: 30px;\n    -webkit-transition: left .3s ease-in-out;\n    transition: left .3s ease-in-out;\n    background: #f0f0f0;\n}\n\n.content {\n    width: auto;\n    height: 100%;\n    padding: 10px;\n    overflow-y: scroll;\n    box-sizing: border-box;\n}\n\n.content-collapse {\n    left: 65px;\n}\n\n.container {\n    padding: 30px;\n    background: #fff;\n    border: 1px solid #ddd;\n    border-radius: 5px;\n}\n\n.crumbs {\n    margin: 10px 0;\n}\n\n.el-table th {\n    background-color: #f5f7fa !important;\n}\n\n.pagination {\n    margin: 20px 0;\n    text-align: right;\n}\n\n.plugins-tips {\n    padding: 20px 10px;\n    margin-bottom: 20px;\n}\n\n.el-button+.el-tooltip {\n    margin-left: 10px;\n}\n\n.el-table tr:hover {\n    background: #f6faff;\n}\n\n.mgb20 {\n    margin-bottom: 20px;\n}\n\n.move-enter-active,\n.move-leave-active {\n    transition: opacity .1s ease;\n}\n\n.move-enter-from,\n.move-leave-to {\n    opacity: 0;\n}\n\n/*BaseForm*/\n\n.form-box {\n    width: 600px;\n}\n\n.form-box .line {\n    text-align: center;\n}\n\n.el-time-panel__content::after,\n.el-time-panel__content::before {\n    margin-top: -7px;\n}\n\n.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {\n    padding-bottom: 0;\n}\n\n\n[class*=\" el-icon-\"], [class^=el-icon-] {\n    speak: none;\n    font-style: normal;\n    font-weight: 400;\n    font-variant: normal;\n    text-transform: none;\n    line-height: 1;\n    vertical-align: baseline;\n    display: inline-block;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n}\n.el-sub-menu [class^=el-icon-] {\n    vertical-align: middle;\n    margin-right: 5px;\n    width: 24px;\n    text-align: center;\n    font-size: 18px;\n}\n\n[hidden]{\n    display: none !important;\n}"
  },
  {
    "path": "detection-fontend/src/components/header.vue",
    "content": "<template>\n\t<div class=\"header\">\n\t\t<!-- 折叠按钮 -->\n\t\t<div class=\"collapse-btn\" @click=\"collapseChage\">\n\t\t\t<el-icon v-if=\"sidebar.collapse\"><Expand /></el-icon>\n\t\t\t<el-icon v-else><Fold /></el-icon>\n\t\t</div>\n\t\t<div class=\"logo\">Yolo通用实现平台</div>\n\t\t<div class=\"header-right\">\n\t\t\t<div class=\"header-user-con\">\n\t\t\t\t<!-- 消息中心 -->\n<!--\t\t\t\t<div class=\"btn-bell\" @click=\"router.push('/tabs')\">-->\n<!--\t\t\t\t\t<el-tooltip-->\n<!--\t\t\t\t\t\teffect=\"dark\"-->\n<!--\t\t\t\t\t\t:content=\"message ? `有${message}条未读消息` : `消息中心`\"-->\n<!--\t\t\t\t\t\tplacement=\"bottom\"-->\n<!--\t\t\t\t\t>-->\n<!--\t\t\t\t\t\t<i class=\"el-icon-lx-notice\"></i>-->\n<!--\t\t\t\t\t</el-tooltip>-->\n<!--\t\t\t\t\t<span class=\"btn-bell-badge\" v-if=\"message\"></span>-->\n<!--\t\t\t\t</div>-->\n\t\t\t\t<!-- 用户头像 -->\n\t\t\t\t<el-avatar class=\"user-avator\" :size=\"30\" :src=\"imgurl\" />\n\t\t\t\t<!-- 用户名下拉菜单 -->\n\t\t\t\t<el-dropdown class=\"user-name\" trigger=\"click\" @command=\"handleCommand\">\n\t\t\t\t\t<span class=\"el-dropdown-link\">\n\t\t\t\t\t\t{{ username }}\n\t\t\t\t\t\t<el-icon class=\"el-icon--right\">\n\t\t\t\t\t\t\t<arrow-down />\n\t\t\t\t\t\t</el-icon>\n\t\t\t\t\t</span>\n\t\t\t\t\t<template #dropdown>\n\t\t\t\t\t\t<el-dropdown-menu>\n\t\t\t\t\t\t\t<a href=\"https://github.com/datar5/yolov8-flask-vue-deploy\" target=\"_blank\">\n\t\t\t\t\t\t\t\t<el-dropdown-item>项目仓库</el-dropdown-item>\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<el-dropdown-item command=\"user\">个人中心</el-dropdown-item>\n\t\t\t\t\t\t\t<el-dropdown-item divided command=\"loginout\">退出登录</el-dropdown-item>\n\t\t\t\t\t\t</el-dropdown-menu>\n\t\t\t\t\t</template>\n\t\t\t\t</el-dropdown>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n<script setup lang=\"ts\">\nimport { onMounted } from 'vue';\nimport { useSidebarStore } from '../store/sidebar';\nimport { useRouter } from 'vue-router';\n// import imgurl from '../assets/img/img.jpg';\nimport imgurl from '../assets/img/img.png';\n\nconst username: string | null = localStorage.getItem('ms_username');\nconst message: number = 2;\n\nconst sidebar = useSidebarStore();\n// 侧边栏折叠\nconst collapseChage = () => {\n\tsidebar.handleCollapse();\n};\n\nonMounted(() => {\n\tif (document.body.clientWidth < 1500) {\n\t\tcollapseChage();\n\t}\n});\n\n// 用户名下拉菜单选择事件\nconst router = useRouter();\nconst handleCommand = (command: string) => {\n\tif (command == 'loginout') {\n\t\tlocalStorage.removeItem('ms_username');\n\t\trouter.push('/login');\n\t} else if (command == 'user') {\n\t\trouter.push('/user');\n\t}\n};\n</script>\n<style scoped>\n.header {\n\tposition: relative;\n\tbox-sizing: border-box;\n\twidth: 100%;\n\theight: 70px;\n\tfont-size: 22px;\n\tcolor: #fff;\n}\n.collapse-btn {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\theight: 100%;\n\tfloat: left;\n\tpadding: 0 21px;\n\tcursor: pointer;\n}\n.header .logo {\n\tfloat: left;\n\twidth: 250px;\n\tline-height: 70px;\n}\n.header-right {\n\tfloat: right;\n\tpadding-right: 50px;\n}\n.header-user-con {\n\tdisplay: flex;\n\theight: 70px;\n\talign-items: center;\n}\n.btn-fullscreen {\n\ttransform: rotate(45deg);\n\tmargin-right: 5px;\n\tfont-size: 24px;\n}\n.btn-bell,\n.btn-fullscreen {\n\tposition: relative;\n\twidth: 30px;\n\theight: 30px;\n\ttext-align: center;\n\tborder-radius: 15px;\n\tcursor: pointer;\n\tdisplay: flex;\n\talign-items: center;\n}\n.btn-bell-badge {\n\tposition: absolute;\n\tright: 4px;\n\ttop: 0px;\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 4px;\n\tbackground: #f56c6c;\n\tcolor: #fff;\n}\n.btn-bell .el-icon-lx-notice {\n\tcolor: #fff;\n}\n.user-name {\n\tmargin-left: 10px;\n}\n.user-avator {\n\tmargin-left: 20px;\n}\n.el-dropdown-link {\n\tcolor: #fff;\n\tcursor: pointer;\n\tdisplay: flex;\n\talign-items: center;\n}\n.el-dropdown-menu__item {\n\ttext-align: center;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/components/sidebar.vue",
    "content": "<template>\n    <div class=\"sidebar\">\n        <el-menu\n            class=\"sidebar-el-menu\"\n            :default-active=\"onRoutes\"\n            :collapse=\"sidebar.collapse\"\n            background-color=\"#324157\"\n            text-color=\"#bfcbd9\"\n            active-text-color=\"#20a0ff\"\n            unique-opened\n            router\n        >\n            <template v-for=\"item in items\">\n                <template v-if=\"item.subs\">\n                    <el-sub-menu :index=\"item.index\" :key=\"item.index\" v-permiss=\"item.permiss\">\n                        <template #title>\n                            <el-icon>\n                                <component :is=\"item.icon\"></component>\n                            </el-icon>\n                            <span>{{ item.title }}</span>\n                        </template>\n                        <template v-for=\"subItem in item.subs\">\n                            <el-sub-menu\n                                v-if=\"subItem.subs\"\n                                :index=\"subItem.index\"\n                                :key=\"subItem.index\"\n                                v-permiss=\"item.permiss\"\n                            >\n                                <template #title>{{ subItem.title }}</template>\n                                <el-menu-item v-for=\"(threeItem, i) in subItem.subs\" :key=\"i\" :index=\"threeItem.index\">\n                                    {{ threeItem.title }}\n                                </el-menu-item>\n                            </el-sub-menu>\n                            <el-menu-item v-else :index=\"subItem.index\" v-permiss=\"item.permiss\">\n                                {{ subItem.title }}\n                            </el-menu-item>\n                        </template>\n                    </el-sub-menu>\n                </template>\n                <template v-else>\n                    <el-menu-item :index=\"item.index\" :key=\"item.index\" v-permiss=\"item.permiss\">\n                        <el-icon>\n                            <component :is=\"item.icon\"></component>\n                        </el-icon>\n                        <template #title>{{ item.title }}</template>\n                    </el-menu-item>\n                </template>\n            </template>\n        </el-menu>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport { useSidebarStore } from '../store/sidebar';\nimport { useRoute } from 'vue-router';\n\nconst items = [\n    // {\n    //     icon: 'Odometer',\n    //     index: '/dashboard',\n    //     title: '系统首页',\n    //     permiss: '1',\n    // },\n    // {\n    //     icon: 'Calendar',\n    //     index: '1',\n    //     title: '表格相关',\n    //     permiss: '2',\n    //     subs: [\n    //         {\n    //             index: '/table',\n    //             title: '常用表格',\n    //             permiss: '2',\n    //         },\n    //         {\n    //             index: '/import',\n    //             title: '导入Excel',\n    //             permiss: '2',\n    //         },\n    //         {\n    //             index: '/export',\n    //             title: '导出Excel',\n    //             permiss: '2',\n    //         },\n    //     ],\n    // },\n    // {\n    //     icon: 'DocumentCopy',\n    //     index: '/tabs',\n    //     title: 'tab选项卡',\n    //     permiss: '3',\n    // },\n    // {\n    //     icon: 'Edit',\n    //     index: '3',\n    //     title: '表单相关',\n    //     permiss: '4',\n    //     subs: [\n    //         {\n    //             index: '/form',\n    //             title: '基本表单',\n    //             permiss: '5',\n    //         },\n    //         {\n    //             index: '/upload',\n    //             title: '文件上传',\n    //             permiss: '6',\n    //         },\n    //         {\n    //             index: '4',\n    //             title: '三级菜单',\n    //             permiss: '7',\n    //             subs: [\n    //                 {\n    //                     index: '/editor',\n    //                     title: '富文本编辑器',\n    //                     permiss: '8',\n    //                 },\n    //                 {\n    //                     index: '/markdown',\n    //                     title: 'markdown编辑器',\n    //                     permiss: '9',\n    //                 },\n    //             ],\n    //         },\n    //     ],\n    // },\n    // {\n    //     icon: 'Setting',\n    //     index: '/icon',\n    //     title: '自定义图标',\n    //     permiss: '10',\n    // },\n    // {\n    //     icon: 'PieChart',\n    //     index: '/charts',\n    //     title: 'schart图表',\n    //     permiss: '11',\n    // },\n    // {\n    //     icon: 'Warning',\n    //     index: '/permission',\n    //     title: '权限管理',\n    //     permiss: '13',\n    // },\n    // {\n    //     icon: 'CoffeeCup',\n    //     index: '/donate',\n    //     title: '支持作者',\n    //     permiss: '14',\n    // },\n    {\n      icon: 'UploadFilled',\n      index: '/uploadFile',\n      title: '图片管理',\n      permiss: '15'\n    },\n    {\n      icon: 'Search',\n      index: '/detection',\n      title: '目标检测',\n      permiss: '16'\n    },\n    {\n      icon: 'Notebook',\n      index: '/resultManager',\n      title: '记录管理',\n      permiss: '1'\n    }\n];\n\nconst route = useRoute();\nconst onRoutes = computed(() => {\n    return route.path;\n});\n\nconst sidebar = useSidebarStore();\n</script>\n\n<style scoped>\n.sidebar {\n    display: block;\n    position: absolute;\n    left: 0;\n    top: 70px;\n    bottom: 0;\n    overflow-y: scroll;\n}\n.sidebar::-webkit-scrollbar {\n    width: 0;\n}\n.sidebar-el-menu:not(.el-menu--collapse) {\n    width: 250px;\n}\n.sidebar > ul {\n    height: 100%;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/components/tags.vue",
    "content": "<template>\n\t<div class=\"tags\" v-if=\"tags.show\">\n\t\t<ul>\n\t\t\t<li\n\t\t\t\tclass=\"tags-li\"\n\t\t\t\tv-for=\"(item, index) in tags.list\"\n\t\t\t\t:class=\"{ active: isActive(item.path) }\"\n\t\t\t\t:key=\"index\"\n\t\t\t>\n\t\t\t\t<router-link :to=\"item.path\" class=\"tags-li-title\">{{ item.title }}</router-link>\n\t\t\t\t<el-icon @click=\"closeTags(index)\"><Close /></el-icon>\n\t\t\t</li>\n\t\t</ul>\n\t\t<div class=\"tags-close-box\">\n\t\t\t<el-dropdown @command=\"handleTags\">\n\t\t\t\t<el-button size=\"small\" type=\"primary\">\n\t\t\t\t\t标签选项\n\t\t\t\t\t<el-icon class=\"el-icon--right\">\n\t\t\t\t\t\t<arrow-down />\n\t\t\t\t\t</el-icon>\n\t\t\t\t</el-button>\n\t\t\t\t<template #dropdown>\n\t\t\t\t\t<el-dropdown-menu size=\"small\">\n\t\t\t\t\t\t<el-dropdown-item command=\"other\">关闭其他</el-dropdown-item>\n\t\t\t\t\t\t<el-dropdown-item command=\"all\">关闭所有</el-dropdown-item>\n\t\t\t\t\t</el-dropdown-menu>\n\t\t\t\t</template>\n\t\t\t</el-dropdown>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\">\nimport { useTagsStore } from '../store/tags';\nimport { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';\n\nconst route = useRoute();\nconst router = useRouter();\nconst isActive = (path: string) => {\n\treturn path === route.fullPath;\n};\n\nconst tags = useTagsStore();\n// 关闭单个标签\nconst closeTags = (index: number) => {\n\tconst delItem = tags.list[index];\n\ttags.delTagsItem(index);\n\tconst item = tags.list[index] ? tags.list[index] : tags.list[index - 1];\n\tif (item) {\n\t\tdelItem.path === route.fullPath && router.push(item.path);\n\t} else {\n\t\trouter.push('/');\n\t}\n};\n\n// 设置标签\nconst setTags = (route: any) => {\n\tconst isExist = tags.list.some(item => {\n\t\treturn item.path === route.fullPath;\n\t});\n\tif (!isExist) {\n\t\tif (tags.list.length >= 8) tags.delTagsItem(0);\n\t\ttags.setTagsItem({\n\t\t\tname: route.name,\n\t\t\ttitle: route.meta.title,\n\t\t\tpath: route.fullPath\n\t\t});\n\t}\n};\nsetTags(route);\nonBeforeRouteUpdate(to => {\n\tsetTags(to);\n});\n\n// 关闭全部标签\nconst closeAll = () => {\n\ttags.clearTags();\n\trouter.push('/');\n};\n// 关闭其他标签\nconst closeOther = () => {\n\tconst curItem = tags.list.filter(item => {\n\t\treturn item.path === route.fullPath;\n\t});\n\ttags.closeTagsOther(curItem);\n};\nconst handleTags = (command: string) => {\n\tcommand === 'other' ? closeOther() : closeAll();\n};\n\n// 关闭当前页面的标签页\n// tags.closeCurrentTag({\n//     $router: router,\n//     $route: route\n// });\n</script>\n\n<style>\n.tags {\n\tposition: relative;\n\theight: 30px;\n\toverflow: hidden;\n\tbackground: #fff;\n\tpadding-right: 120px;\n\tbox-shadow: 0 5px 10px #ddd;\n}\n\n.tags ul {\n\tbox-sizing: border-box;\n\twidth: 100%;\n\theight: 100%;\n}\n\n.tags-li {\n\tdisplay: flex;\n\talign-items: center;\n\tfloat: left;\n\tmargin: 3px 5px 2px 3px;\n\tborder-radius: 3px;\n\tfont-size: 12px;\n\toverflow: hidden;\n\tcursor: pointer;\n\theight: 23px;\n\tborder: 1px solid #e9eaec;\n\tbackground: #fff;\n\tpadding: 0 5px 0 12px;\n\tcolor: #666;\n\t-webkit-transition: all 0.3s ease-in;\n\t-moz-transition: all 0.3s ease-in;\n\ttransition: all 0.3s ease-in;\n}\n\n.tags-li:not(.active):hover {\n\tbackground: #f8f8f8;\n}\n\n.tags-li.active {\n\tcolor: #fff;\n}\n\n.tags-li-title {\n\tfloat: left;\n\tmax-width: 80px;\n\toverflow: hidden;\n\twhite-space: nowrap;\n\ttext-overflow: ellipsis;\n\tmargin-right: 5px;\n\tcolor: #666;\n}\n\n.tags-li.active .tags-li-title {\n\tcolor: #fff;\n}\n\n.tags-close-box {\n\tposition: absolute;\n\tright: 0;\n\ttop: 0;\n\tbox-sizing: border-box;\n\tpadding-top: 1px;\n\ttext-align: center;\n\twidth: 110px;\n\theight: 30px;\n\tbackground: #fff;\n\tbox-shadow: -3px 0 15px 3px rgba(0, 0, 0, 0.1);\n\tz-index: 10;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/main.ts",
    "content": "import { createApp } from 'vue';\nimport { createPinia } from 'pinia';\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue';\nimport App from './App.vue';\nimport router from './router';\nimport { usePermissStore } from './store/permiss';\nimport 'element-plus/dist/index.css';\nimport './assets/css/icon.css';\n\nimport axios from \"axios\";\n\naxios.defaults.baseURL = \"http://localhost:5000\"\naxios.defaults.headers[\"Content-Type\"] =  \"application/json;charset=UTF-8\"\n\n\nconst app = createApp(App);\napp.use(createPinia());\napp.use(router);\n\n// 注册elementplus图标\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n    app.component(key, component);\n}\n// 自定义权限指令\nconst permiss = usePermissStore();\napp.directive('permiss', {\n    mounted(el, binding) {\n        if (!permiss.key.includes(String(binding.value))) {\n            el['hidden'] = true;\n        }\n    },\n});\n\napp.mount('#app');\n"
  },
  {
    "path": "detection-fontend/src/router/index.ts",
    "content": "import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';\nimport { usePermissStore } from '../store/permiss';\nimport Home from '../views/home.vue';\n\nconst routes: RouteRecordRaw[] = [\n    {\n        path: '/',\n        redirect: '/detection',\n    },\n    {\n        path: '/',\n        name: 'Home',\n        component: Home,\n        children: [\n            // {\n            //     path: '/dashboard',\n            //     name: 'dashboard',\n            //     meta: {\n            //         title: '系统首页',\n            //         permiss: '1',\n            //     },\n            //     component: () => import(/* webpackChunkName: \"dashboard\" */ '../views/dashboard.vue'),\n            // },\n            {\n                path: '/table',\n                name: 'basetable',\n                meta: {\n                    title: '表格',\n                    permiss: '2',\n                },\n                component: () => import(/* webpackChunkName: \"table\" */ '../views/table.vue'),\n            },\n            {\n                path: '/charts',\n                name: 'basecharts',\n                meta: {\n                    title: '图表',\n                    permiss: '11',\n                },\n                component: () => import(/* webpackChunkName: \"charts\" */ '../views/charts.vue'),\n            },\n            {\n                path: '/form',\n                name: 'baseform',\n                meta: {\n                    title: '表单',\n                    permiss: '5',\n                },\n                component: () => import(/* webpackChunkName: \"form\" */ '../views/form.vue'),\n            },\n            {\n                path: '/tabs',\n                name: 'tabs',\n                meta: {\n                    title: 'tab标签',\n                    permiss: '3',\n                },\n                component: () => import(/* webpackChunkName: \"tabs\" */ '../views/tabs.vue'),\n            },\n            {\n                path: '/donate',\n                name: 'donate',\n                meta: {\n                    title: '鼓励作者',\n                    permiss: '14',\n                },\n                component: () => import(/* webpackChunkName: \"donate\" */ '../views/donate.vue'),\n            },\n            {\n                path: '/permission',\n                name: 'permission',\n                meta: {\n                    title: '权限管理',\n                    permiss: '13',\n                },\n                component: () => import(/* webpackChunkName: \"permission\" */ '../views/permission.vue'),\n            },\n            {\n                path: '/upload',\n                name: 'upload',\n                meta: {\n                    title: '上传插件',\n                    permiss: '6',\n                },\n                component: () => import(/* webpackChunkName: \"upload\" */ '../views/upload.vue'),\n            },\n            {\n                path: '/icon',\n                name: 'icon',\n                meta: {\n                    title: '自定义图标',\n                    permiss: '10',\n                },\n                component: () => import(/* webpackChunkName: \"icon\" */ '../views/icon.vue'),\n            },\n            {\n                path: '/user',\n                name: 'user',\n                meta: {\n                    title: '个人中心',\n                },\n                component: () => import(/* webpackChunkName: \"user\" */ '../views/user.vue'),\n            },\n            {\n                path: '/editor',\n                name: 'editor',\n                meta: {\n                    title: '富文本编辑器',\n                    permiss: '8',\n                },\n                component: () => import(/* webpackChunkName: \"editor\" */ '../views/editor.vue'),\n            },\n            {\n                path: '/markdown',\n                name: 'markdown',\n                meta: {\n                    title: 'markdown编辑器',\n                    permiss: '9',\n                },\n                component: () => import(/* webpackChunkName: \"markdown\" */ '../views/markdown.vue'),\n            },\n            {\n                path: '/export',\n                name: 'export',\n                meta: {\n                    title: '导出Excel',\n                    permiss: '2',\n                },\n                component: () => import(/* webpackChunkName: \"export\" */ '../views/export.vue'),\n            },\n            {\n                path: '/import',\n                name: 'import',\n                meta: {\n                    title: '导入Excel',\n                    permiss: '2',\n                },\n                component: () => import(/* webpackChunkName: \"import\" */ '../views/import.vue'),\n            },\n            {\n                path: '/uploadFile',\n                name: 'uploadFile',\n                meta: {\n                    title: '上传文件',\n                    permiss: '2',\n                },\n                component: () => import(/* webpackChunkName: \"import\" */ '../viewDetect/uploadFile.vue'),\n            },\n            {\n                path: '/detection',\n                name: 'detection',\n                meta: {\n                    title: '目标检测',\n                    permiss: '1',\n                },\n                component: () => import(/* webpackChunkName: \"import\" */ '../viewDetect/detectVue.vue'),\n            },\n            {\n                path: '/resultManager',\n                name: 'resultManager',\n                meta: {\n                    title: '记录管理',\n                    permiss: '3',\n                },\n                component: () => import(/* webpackChunkName: \"import\" */ '../viewDetect/ResultVue.vue'),\n            },\n        ],\n    },\n    {\n        path: '/login',\n        name: 'Login',\n        meta: {\n            title: '登录',\n        },\n        component: () => import(/* webpackChunkName: \"login\" */ '../views/login.vue'),\n    },\n    {\n        path: '/403',\n        name: '403',\n        meta: {\n            title: '没有权限',\n        },\n        component: () => import(/* webpackChunkName: \"403\" */ '../views/403.vue'),\n    },\n];\n\nconst router = createRouter({\n    history: createWebHashHistory(),\n    routes,\n});\n\nrouter.beforeEach((to, from, next) => {\n    document.title = `${to.meta.title} | vue-manage-system`;\n    const role = localStorage.getItem('ms_username');\n    const permiss = usePermissStore();\n    if (!role && to.path !== '/login') {\n        next('/login');\n    } else if (to.meta.permiss && !permiss.key.includes(to.meta.permiss)) {\n        // 如果没有权限，则进入403\n        next('/403');\n    } else {\n        next();\n    }\n});\n\nexport default router;\n"
  },
  {
    "path": "detection-fontend/src/store/permiss.ts",
    "content": "import { defineStore } from 'pinia';\n\ninterface ObjectList {\n\t[key: string]: string[];\n}\n\nexport const usePermissStore = defineStore('permiss', {\n\tstate: () => {\n\t\tconst keys = localStorage.getItem('ms_keys');\n\t\treturn {\n\t\t\tkey: keys ? JSON.parse(keys) : <string[]>[],\n\t\t\tdefaultList: <ObjectList>{\n\t\t\t\tadmin: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16'],\n\t\t\t\tuser: ['1', '2', '3', '11', '13', '14', '15']\n\t\t\t}\n\t\t};\n\t},\n\tactions: {\n\t\thandleSet(val: string[]) {\n\t\t\tthis.key = val;\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "detection-fontend/src/store/sidebar.ts",
    "content": "import { defineStore } from 'pinia';\n\nexport const useSidebarStore = defineStore('sidebar', {\n\tstate: () => {\n\t\treturn {\n\t\t\tcollapse: false\n\t\t};\n\t},\n\tgetters: {},\n\tactions: {\n\t\thandleCollapse() {\n\t\t\tthis.collapse = !this.collapse;\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "detection-fontend/src/store/tags.ts",
    "content": "import { defineStore } from 'pinia';\n\ninterface ListItem {\n\tname: string;\n\tpath: string;\n\ttitle: string;\n}\n\nexport const useTagsStore = defineStore('tags', {\n\tstate: () => {\n\t\treturn {\n\t\t\tlist: <ListItem[]>[]\n\t\t};\n\t},\n\tgetters: {\n\t\tshow: state => {\n\t\t\treturn state.list.length > 0;\n\t\t},\n\t\tnameList: state => {\n\t\t\treturn state.list.map(item => item.name);\n\t\t}\n\t},\n\tactions: {\n\t\tdelTagsItem(index: number) {\n\t\t\tthis.list.splice(index, 1);\n\t\t},\n\t\tsetTagsItem(data: ListItem) {\n\t\t\tthis.list.push(data);\n\t\t},\n\t\tclearTags() {\n\t\t\tthis.list = [];\n\t\t},\n\t\tcloseTagsOther(data: ListItem[]) {\n\t\t\tthis.list = data;\n\t\t},\n\t\tcloseCurrentTag(data: any) {\n\t\t\tfor (let i = 0, len = this.list.length; i < len; i++) {\n\t\t\t\tconst item = this.list[i];\n\t\t\t\tif (item.path === data.$route.fullPath) {\n\t\t\t\t\tif (i < len - 1) {\n\t\t\t\t\t\tdata.$router.push(this.list[i + 1].path);\n\t\t\t\t\t} else if (i > 0) {\n\t\t\t\t\t\tdata.$router.push(this.list[i - 1].path);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdata.$router.push('/');\n\t\t\t\t\t}\n\t\t\t\t\tthis.list.splice(i, 1);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "detection-fontend/src/utils/request.ts",
    "content": "import axios, {AxiosInstance, AxiosError, AxiosResponse, AxiosRequestConfig} from 'axios';\n\nconst service:AxiosInstance = axios.create({\n    timeout: 5000\n});\n\nservice.interceptors.request.use(\n    (config: AxiosRequestConfig) => {\n        return config;\n    },\n    (error: AxiosError) => {\n        console.log(error);\n        return Promise.reject();\n    }\n);\n\nservice.interceptors.response.use(\n    (response: AxiosResponse) => {\n        if (response.status === 200) {\n            return response;\n        } else {\n            Promise.reject();\n        }\n    },\n    (error: AxiosError) => {\n        console.log(error);\n        return Promise.reject();\n    }\n);\n\nexport default service;\n"
  },
  {
    "path": "detection-fontend/src/viewDetect/ResultVue.vue",
    "content": "<template>\n  <div class=\"container\">\n    <div>\n      <div class=\"content-title\">模型信息</div>\n      <div class=\"handle-box\">\n        <el-input v-model=\"model_query.keyword\" placeholder=\"模型名称\" class=\"handle-input mr10\"></el-input>\n        <el-button type=\"primary\" :icon=\"Search \" @click=\"handleModelSearch\">搜索</el-button>\n\n        <el-button type=\"success\"  @click=\"handleModelAll\">\n          <el-icon><List /></el-icon>\n          <span>列出模型</span>\n        </el-button>\n      </div>\n\n\n      <el-table :data=\"modelData\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n        <el-table-column prop=\"wid\" label=\"模型ID\" width=\"55\" align=\"center\"></el-table-column>\n        <el-table-column prop=\"model_name\" label=\"文件名\"></el-table-column>\n\n\n        <el-table-column prop=\"model_type\" label=\"模型类别\"></el-table-column>\n        <el-table-column prop=\"model_dataset\" label=\"对应数据集\"></el-table-column>\n\n\n<!--        <el-table-column label=\"操作\" width=\"300\" align=\"center\">-->\n<!--          <template #default=\"scope\">-->\n<!--            <el-button text :icon=\"Search\" class=\"green\" @click=\"handleModelDetail(scope.$index, scope.row)\" v-permiss=\"15\">-->\n<!--              查看-->\n<!--            </el-button>-->\n<!--          </template>-->\n<!--        </el-table-column>-->\n      </el-table>\n\n      <div class=\"pagination\">\n        <el-pagination\n            background\n            layout=\"total, sizes, prev, pager, next, jumper\"\n            v-model:current-page=\"model_query.curPage\"\n            v-model:page-size=\"model_query.pageSize\"\n\n            :page-sizes=\"[5, 10]\"\n            :total=\"modelTotal\"\n            @current-change=\"handleModelPageChange\"\n            @size-change=\"handleModelSizeChange\"\n\n        ></el-pagination>\n      </div>\n    </div>\n\n\n    <div class=\"content-title\">图片查看</div>\n    <div class=\"handle-box\">\n      <el-input v-model=\"query.keyword\" placeholder=\"文件名称\" class=\"handle-input mr10\"></el-input>\n      <el-button type=\"primary\" :icon=\"Search \" @click=\"handleSearch\">搜索</el-button>\n\n<!--      <el-button type=\"primary\"  @click=\"getData\">-->\n<!--        <el-icon><Refresh /></el-icon>-->\n<!--        <span>刷新页面</span>-->\n<!--      </el-button>-->\n\n      <el-button type=\"success\"  @click=\"handleAll\">\n        <el-icon><List /></el-icon>\n        <span>列出全部</span>\n      </el-button>\n    </div>\n\n    <el-table :data=\"tableData\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n      <el-table-column prop=\"fid\" label=\"文件ID\" width=\"55\" align=\"center\"></el-table-column>\n      <el-table-column prop=\"name\" label=\"文件名\"></el-table-column>\n\n      <el-table-column label=\"图片(查看大图)\" align=\"center\">\n        <template #default=\"scope\">\n          <el-image\n              class=\"table-td-thumb\"\n              :src=\"scope.row.img_url\"\n              :z-index=\"10\"\n              :preview-src-list=\"[scope.row.img_url]\"\n              preview-teleported\n          >\n          </el-image>\n        </template>\n      </el-table-column>\n\n      <el-table-column label=\"时间\">\n        <template #default=\"scope\">{{ scope.row.timestamp }}</template>\n      </el-table-column>\n\n      <el-table-column prop=\"type\" label=\"文件类别\"></el-table-column>\n      <el-table-column prop=\"origin\" label=\"文件来源\"></el-table-column>\n      <el-table-column prop=\"width\" label=\"宽度\"></el-table-column>\n      <el-table-column prop=\"height\" label=\"高度\"></el-table-column>\n\n    </el-table>\n\n    <div class=\"pagination\">\n      <el-pagination\n          background\n          layout=\"total, sizes, prev, pager, next, jumper\"\n          v-model:current-page=\"query.curPage\"\n          v-model:page-size=\"query.pageSize\"\n\n          :page-sizes=\"[5, 10]\"\n          :total=\"pageTotal\"\n          @current-change=\"handlePageChange\"\n          @size-change=\"handleSizeChange\"\n\n      ></el-pagination>\n    </div>\n\n    <div class=\"content-title\">记录管理</div>\n\n    <div class=\"handle-box\">\n      <el-input v-model=\"result_query.keyword\" placeholder=\"结果名称\" class=\"handle-input mr10\"></el-input>\n      <el-button type=\"primary\" :icon=\"Search \" @click=\"handleResultSearch\">搜索</el-button>\n\n      <el-button type=\"success\"  @click=\"handleResultAll\">\n        <el-icon><List /></el-icon>\n        <span>列出结果</span>\n      </el-button>\n    </div>\n\n    <el-table :data=\"resultData\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n      <el-table-column prop=\"rid\" label=\"结果ID\" width=\"55\" align=\"center\"></el-table-column>\n      <el-table-column prop=\"wid\" label=\"模型ID\"></el-table-column>\n\n      <el-table-column label=\"图片(查看大图)\" align=\"center\">\n        <template #default=\"scope\">\n          <el-image\n              class=\"table-td-thumb\"\n              :src=\"scope.row.img_url\"\n              :z-index=\"10\"\n              :preview-src-list=\"[scope.row.img_url]\"\n              preview-teleported\n          >\n          </el-image>\n        </template>\n      </el-table-column>\n\n      <el-table-column label=\"时间\">\n        <template #default=\"scope\">{{ dateForm(scope.row.timestamp) }}</template>\n      </el-table-column>\n\n      <el-table-column prop=\"type\" label=\"文件类别\"></el-table-column>\n      <el-table-column prop=\"addition\" label=\"额外说明\"></el-table-column>\n      <el-table-column prop=\"mission_type\" label=\"任务类型\"></el-table-column>\n      <el-table-column prop=\"filename\" label=\"文件来源\"></el-table-column>\n      <el-table-column prop=\"num\" label=\"检测数目\"></el-table-column>\n\n      <el-table-column label=\"操作\" width=\"300\" align=\"center\">\n        <template #default=\"scope\">\n          <el-button text :icon=\"TopLeft\" class=\"primary\" @click=\"handleResultDetail(scope.row)\" v-permiss=\"15\">\n            查看\n          </el-button>\n          <el-button text :icon=\"Edit\" @click=\"handleResultEdit(scope.row)\" v-permiss=\"15\">\n            编辑\n          </el-button>\n          <el-button text :icon=\"Delete\" class=\"red\" @click=\"handleResultDelete(scope.row)\" v-permiss=\"16\">\n            删除\n          </el-button>\n        </template>\n      </el-table-column>\n\n    </el-table>\n\n    <div class=\"pagination\">\n      <el-pagination\n          background\n          layout=\"total, sizes, prev, pager, next, jumper\"\n          v-model:current-page=\"result_query.curPage\"\n          v-model:page-size=\"result_query.pageSize\"\n\n          :page-sizes=\"[5, 10]\"\n          :total=\"resultTotal\"\n          @current-change=\"handleResultPageChange\"\n          @size-change=\"handleResultSizeChange\"\n\n      ></el-pagination>\n    </div>\n\n    <el-dialog title=\"结果编辑\" v-model=\"resultEditVisible\" width=\"30%\">\n      <el-form label-width=\"100px\">\n        <el-form-item label=\"结果ID\">\n          <span>{{resultEditForm.rid}}</span>\n        </el-form-item>\n\n        <el-form-item label=\"模型ID\">\n          <span>{{resultEditForm.wid}}</span>\n        </el-form-item>\n\n        <el-form-item label=\"时间\">\n          <span>{{ dateForm(resultEditForm.timestamp) }}</span>\n        </el-form-item>\n\n        <el-form-item label=\"文件类别\">\n          <span>{{resultEditForm.type}}</span>\n        </el-form-item>\n\n\n        <el-form-item label=\"额外说明\">\n          <el-input v-model=\"resultEditForm.addition\"></el-input>\n        </el-form-item>\n\n        <el-form-item label=\"任务类别\">\n          <el-input v-model=\"resultEditForm.mission_type\"></el-input>\n        </el-form-item>\n\n        <el-form-item label=\"检测数目\">\n          <span>{{ resultEditForm.num }}</span>\n        </el-form-item>\n\n      </el-form>\n      <template #footer>\n          <span class=\"dialog-footer\">\n            <el-button @click=\"submitEdit\" type=\"primary\">提交</el-button>\n            <el-button @click=\"resultEditVisible = false\" >退出</el-button>\n          </span>\n      </template>\n    </el-dialog>\n\n    <el-dialog title=\"结果细节\" v-model=\"objDetail\" width=\"70%\" class=\"content-title\">\n\n      <div class=\"content-title\">原图显示</div>\n      <div class=\"demo-image__lazy\">\n        <el-image v-for=\"url in [obj_query.img_url]\"  :key=\"url\" :src=\"url\" lazy />\n      </div>\n\n      <div class=\"content-title\">任务信息</div>\n      <el-descriptions :column=\"3\" border>\n        <el-descriptions-item\n            label=\"时间\"\n            label-align=\"right\"\n            align=\"center\"\n            label-class-name=\"my-label\"\n            class-name=\"my-content\"\n            width=\"200px\">\n          {{dateForm(resultParams.timestamp)}}\n        </el-descriptions-item>\n\n        <el-descriptions-item label=\"文件类别\" label-align=\"right\" align=\"center\" width=\"100px\">\n          {{resultParams.type}}\n        </el-descriptions-item>\n        <el-descriptions-item label=\"额外说明\" label-align=\"right\" align=\"center\">\n          {{resultParams.addition}}\n        </el-descriptions-item>\n\n        <el-descriptions-item label=\"任务类型\" label-align=\"right\" align=\"center\">\n          <el-tag size=\"large\" type=\"success\">\n          {{resultParams.mission_type}}\n          </el-tag>\n        </el-descriptions-item>\n\n        <el-descriptions-item label=\"文件来源\" label-align=\"right\" align=\"center\">\n          {{resultParams.filename}}\n        </el-descriptions-item>\n      </el-descriptions>\n\n      <div class=\"content-title\">检测模型</div>\n\n      <el-descriptions :column=\"3\" border>\n        <el-descriptions-item\n            label=\"模型ID\"\n            label-align=\"right\"\n            align=\"center\"\n            label-class-name=\"my-label\"\n            class-name=\"my-content\"\n            width=\"150px\">\n          {{model_params.wid}}\n        </el-descriptions-item>\n\n        <el-descriptions-item label=\"文件名\" label-align=\"right\" align=\"center\">\n          {{model_params.model_name}}\n        </el-descriptions-item>\n        <el-descriptions-item label=\"模型类别\" label-align=\"right\" align=\"center\">\n          <el-tag size=\"middle\" type=\"primary\">{{model_params.model_type}}</el-tag>\n        </el-descriptions-item>\n\n        <el-descriptions-item label=\"对应数据集\" label-align=\"right\" align=\"center\">\n          {{model_params.model_dataset}}\n        </el-descriptions-item>\n      </el-descriptions>\n\n      <div class=\"content-title\">物体细节</div>\n\n      <el-table :data=\"objData\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n        <el-table-column prop=\"oid\" label=\"物体ID\"></el-table-column>\n        <el-table-column prop=\"rid\" label=\"结果ID\"></el-table-column>\n        <el-table-column prop=\"cls\" label=\"类别\"></el-table-column>\n        <el-table-column prop=\"conf\" label=\"置信度\"></el-table-column>\n        <el-table-column prop=\"x1\" label=\"左上角横坐标\"></el-table-column>\n        <el-table-column prop=\"y1\" label=\"左上角纵坐标\"></el-table-column>\n        <el-table-column prop=\"x2\" label=\"右下角横坐标\"></el-table-column>\n        <el-table-column prop=\"y2\" label=\"右下角纵坐标\"></el-table-column>\n      </el-table>\n\n      <div class=\"pagination\">\n        <el-pagination\n            background\n            layout=\"total, sizes, prev, pager, next, jumper\"\n            v-model:current-page=\"obj_query.curPage\"\n            v-model:page-size=\"obj_query.pageSize\"\n\n            :page-sizes=\"[5, 10, 20]\"\n            :total=\"objTotal\"\n            @current-change=\"handleObjPageChange\"\n            @size-change=\"handleObjSizeChange\"\n\n        ></el-pagination>\n      </div>\n\n      <template #footer>\n          <span class=\"dialog-footer\">\n            <el-button @click=\"objDetail = false\" >退出</el-button>\n          </span>\n      </template>\n    </el-dialog>\n\n  </div>\n\n</template>\n\n<script lang=\"ts\" setup>\nimport {ref, reactive, onMounted} from 'vue';\nimport {ElMessage, ElMessageBox, FormRules} from 'element-plus';\nimport {Delete, Edit, Search, Plus, TopLeft} from '@element-plus/icons-vue';\nimport axios from \"axios\";\nimport {compileScript} from \"@vue/compiler-sfc\";\n// import { fetchData } from '../api/index';\n\ninterface TableItem { // 定义图片的泛型\n\n  // id: number;\n  // name: string;\n  // money: string;\n  // state: string;\n  // date: string;\n  // address: string;\n  fid: number;\n  height: number;\n  width: number;\n  origin: string;\n  timestamp:string;\n  type: string;\n  name: string;\n  img_url: string;\n}\n\ninterface ModelItem{ // 定义model的接口\n  wid: number,\n  model_name: string,\n  model_type: string,\n  model_dataset: string\n}\n\ninterface ResultItem{ // 定义model的接口\n  rid: number,\n  wid: number,\n  timestamp: number\n  type: string,\n  addtion: string,\n  mission_type: string,\n  filename: string,\n  num: number\n}\n\ninterface ObjItem{ // 定义model的接口\n  oid: number,\n  rid: number,\n  cls: string,\n  x1: number,\n  y1: number,\n  x2: number,\n  y2: number,\n  conf: number\n}\n\n// 下面定义的是file表相关的数据\nconst query = reactive({\n  curPage: 1,\n  pageSize: 5,\n  tableName: \"file\",\n  keyword: \"\"\n});\n\nconst tableData = ref<TableItem[]>([]);  // 表格数据\n\nconst pageTotal = ref(0);\n\n// 下面定义的是model表相关的数据\nconst model_query = reactive({\n  curPage: 1,\n  pageSize: 5,\n  tableName: \"model\",\n  keyword: \"\"\n})\n\nconst modelData = ref<ModelItem[]>([])\nconst modelTotal = ref(0)\n\n// 下面定义的是result表的相关的数据\nconst result_query = reactive({\n  curPage: 1,\n  pageSize: 5,\n  tableName: \"result\",\n  keyword: \"\"\n})\n\nconst resultData = ref<ResultItem[]>([])\nconst resultTotal = ref(0)\n\n\n// 获取file表格数据\nconst getData = () => {\n  // console.log(query)\n\n  if(query.keyword.length == 0){\n    axios({\n      method: 'GET',\n      url: '/page',\n      params: {\n        curPage: query.curPage,\n        pageSize: query.pageSize,\n        tableName: \"file\"\n      },\n    }).then(res=>{\n      tableData.value= res.data.results\n      // console.log(tableData)\n    })\n\n    axios({\n      method: 'GET',\n      url: '/num',\n      params: {\n        tableName: \"file\",\n        rid: -2\n      },\n    }).then(res=>{\n      pageTotal.value= res.data.results\n    })\n  }\n  else{\n    axios({\n      method: 'GET',\n      url: '/deblurS',\n      params: {\n        curPage: query.curPage,\n        pageSize: query.pageSize,\n        keyword: query.keyword,\n        tableName: \"file\"\n      },\n    }).then(res=>{\n      // console.log(res)\n      tableData.value= res.data.results\n      // console.log(tableData)\n    })\n\n    axios({\n      method: 'GET',\n      url: '/deblurSNum',\n      params: {\n        keyword: query.keyword,\n        tableName: \"file\"\n      },\n    }).then(res=>{\n      pageTotal.value= res.data.results\n    })\n  }\n};\n\n// 获得model表格的数据\nconst getModelData = () => {\n  if(model_query.keyword.length == 0){\n    axios({\n      method: 'GET',\n      url: '/page',\n      params: {\n        curPage: model_query.curPage,\n        pageSize: model_query.pageSize,\n        tableName: \"model\"\n      },\n    }).then(res=>{\n      modelData.value= res.data.results\n      // console.log(tableData)\n    })\n\n    axios({\n      method: 'GET',\n      url: '/num',\n      params: {\n        tableName: \"model\",\n        rid: -2\n      },\n    }).then(res=>{\n      modelTotal.value= res.data.results\n    })\n  }\n  else{\n    axios({\n      method: 'GET',\n      url: '/deblurS',\n      params: {\n        curPage: model_query.curPage,\n        pageSize: model_query.pageSize,\n        keyword: model_query.keyword,\n        tableName: \"model\"\n      },\n    }).then(res=>{\n      // console.log(res)\n      modelData.value= res.data.results\n      // console.log(tableData)\n    })\n\n    axios({\n      method: 'GET',\n      url: '/deblurSNum',\n      params: {\n        keyword: model_query.keyword,\n        tableName: \"model\"\n      },\n    }).then(res=>{\n      modelTotal.value= res.data.results\n    })\n  }\n}\n\nconst getResultData = () => {\n  if(result_query.keyword.length == 0){\n    axios({\n      method: 'GET',\n      url: '/page',\n      params: {\n        curPage: result_query.curPage,\n        pageSize: result_query.pageSize,\n        tableName: \"result\"\n      },\n    }).then(res=>{\n      resultData.value= res.data.results\n      // console.log(tableData)\n    })\n\n    axios({\n      method: 'GET',\n      url: '/num',\n      params: {\n        tableName: \"result\",\n        rid: -2\n      },\n    }).then(res=>{\n      resultTotal.value= res.data.results\n    })\n  }\n  else{\n    axios({\n      method: 'GET',\n      url: '/deblurS',\n      params: {\n        curPage: result_query.curPage,\n        pageSize: result_query.pageSize,\n        keyword: result_query.keyword,\n        tableName: \"result\"\n      },\n    }).then(res=>{\n      // console.log(res)\n      resultData.value= res.data.results\n      // console.log(tableData)\n    })\n\n    axios({\n      method: 'GET',\n      url: '/deblurSNum',\n      params: {\n        keyword: result_query.keyword,\n        tableName: \"result\"\n      },\n    }).then(res=>{\n      resultTotal.value= res.data.results\n    })\n  }\n}\n\n\nonMounted(() => {\n  getData()\n  getModelData()\n  getResultData()\n})\n\n// 下面的分页get得到数据操作是针对的file表\n// 模糊查找操作\nconst handleSearch = () => {\n  query.curPage = 1;\n  getData();\n};\n// 列出全部\nconst handleAll = () => {\n  query.keyword = '';\n  query.curPage = 1\n  getData();\n}\n\n// 分页导航\nconst handlePageChange = (val: number) => {\n  query.curPage = val;\n  getData();\n};\n\nconst handleSizeChange  = (val: number) => {\n  query.pageSize = val;\n  getData();\n};\n\n// 下面的分页get得到数据操作是针对的model表\n// 模糊查找操作\nconst handleModelSearch = () => {\n  model_query.curPage = 1;\n  getModelData();\n};\n\n// 列出全部\nconst handleModelAll = () => {\n  model_query.keyword = '';\n  model_query.curPage = 1\n  getModelData();\n}\n\n// 分页导航\nconst handleModelPageChange = (val: number) => {\n  model_query.curPage = val;\n  getModelData();\n};\n\nconst handleModelSizeChange  = (val: number) => {\n  model_query.pageSize = val;\n  getModelData();\n};\n\n\n// 下面的分页get得到数据操作是针对的result表\n\nconst dateForm = (originVal: number) => {  // 时间戳转日期\n      var now = new Date(originVal * 1000)\n      var y = now.getFullYear()\n      var m = now.getMonth() + 1\n      var d = now.getDate()\n      return  y + \"-\" + (m < 10 ? \"0\" + m : m) + \"-\" +\n          (d < 10 ? \"0\" + d : d) + \" \" + now.toTimeString().substr(0, 8);\n}\n\n\nconst handleResultSearch = () => {\n  result_query.curPage = 1;\n  getResultData();\n};\n\n// 列出全部\nconst handleResultAll = () => {\n  result_query.keyword = '';\n  result_query.curPage = 1\n  getResultData();\n}\n\n// 分页导航\nconst handleResultPageChange = (val: number) => {\n  result_query.curPage = val;\n  getResultData();\n};\n\nconst handleResultSizeChange  = (val: number) => {\n  result_query.pageSize = val;\n  getResultData();\n};\n\n/***\n 记录相关操作\n ***/\nconst resultEditVisible = ref(false)\nconst resultEditForm = reactive({\n  rid: -1,\n  wid: -1,\n  timestamp: '',\n  type: '',\n  addition: '',\n  mission_type: '',\n  filename: '',\n  num: -1\n})\n\nconst handleResultEdit = (row: any) => {\n  for(var key in row){\n    resultEditForm[key] = row[key]\n  }\n  resultEditVisible.value = true\n}\n\nconst submitEdit = () => {\n  resultEditVisible.value = false\n  axios({\n    method: 'PUT',\n    url: '/result/' + resultEditForm.rid,\n    data: resultEditForm\n  }).then(res=>{\n    console.log(res)\n    if(res.data.status == 'success'){\n      ElMessage.success('修改成功')\n      getResultData()\n    }\n  })\n}\n\n\n/**\n * 显示物体信息细节\n */\n\n// 下面定义的是obj表的相关数据\nconst obj_query = reactive({\n  curPage: 1,\n  pageSize: 5,\n  tableName: \"obj\",\n  rid: 1\n})\n\nconst model_params = reactive({\n  model_dataset: null,\n  model_name: null,\n  model_type: null,\n  wid: -1\n})\n\nconst resultParams = reactive({\n  timestamp: \"\",\n  type: \"\",\n  addition: \"\",\n  mission_type: \"\",\n  filename: \"\"\n})\n\nconst objData = ref<ObjItem[]>([])\nconst objTotal = ref(0)\nconst objDetail = ref(false)\n\nconst handleResultDetail = (row: any) => {\n  objDetail.value = true\n  for(var key in row){\n    obj_query[key] = row[key]\n    resultParams[key] = row[key]\n  }\n  axios({\n    method: 'GET',\n    url: '/wmodel/' + row.wid\n  }).then(res=>{\n    for(var key in res.data.results){\n      model_params[key] = res.data.results[key]\n    }\n  })\n  getResultDetail(row)\n  objTotal.value = row.num\n  // console.log(row.rid)\n  // console.log({\n  //   'curPage': obj_query.curPage,\n  //   'pageSize': obj_query.pageSize\n  // })\n}\n\nconst getResultDetail = () => {\n  axios({\n    method: 'GET',\n    url: '/objectPage/' + obj_query.rid,\n    params:{\n      'curPage': obj_query.curPage,\n      'pageSize': obj_query.pageSize\n    }\n  }).then(res=>{\n    objData.value = res.data.results\n    for(var i = 0;i < objData.value.length;i++){\n      objData.value[i].conf = objData.value[i].conf.toFixed(3)\n      // console.log('114514')\n    }\n  })\n}\n\nconst handleObjPageChange = (val: number) => {\n  obj_query.curPage = val;\n  getResultDetail()\n};\n\nconst handleObjSizeChange  = (val: number) => {\n  obj_query.pageSize = val;\n  getResultDetail()\n};\n\nconst handleResultDelete = (row: any) =>{\n  ElMessageBox.confirm('确定要删除记录吗？', '提示', {\n    type: 'warning'\n  }).then(()=>{\n    axios({\n      method: 'DELETE',\n      url: '/obj/' + row.rid\n    }).then(res=>{\n      if(res.data.status == 'success'){\n        axios({\n          method: 'DELETE',\n          url: '/result/' + row.rid  // 删除的文件操作和删除的数据操作是耦合的\n        }).then(res=>{\n          if(res.data.status == 'success'){\n            ElMessage.success('记录删除成功')\n            getResultData()\n          }\n          else{\n            ElMessage.error('记录删除出现异常')\n          }\n        })\n      }\n      else{\n        ElMessage.error('记录删除出现异常')\n      }\n    })\n  })\n}\n\n// 查看模型的内部细节\nconst handleModelDetail = (index: number, row: any) =>{\n\n}\n\n\n\n</script>\n\n<style scoped>\n.content-title {\n  font-weight: 400;\n  line-height: 50px;\n  margin: 10px 0;\n  font-size: 22px;\n  color: #1f2f3d;\n}\n.upload-demo {\n  width: 360px;\n}\n\n.handle-box {\n  margin-bottom: 20px;\n}\n\n.handle-select {\n  width: 120px;\n}\n\n.handle-input {\n  width: 300px;\n}\n.table {\n  width: 100%;\n  font-size: 14px;\n}\n.red {\n  color: red;\n}\n\n.primary{\n  color: cornflowerblue;\n}\n\n.green{\n  color: green;\n}\n\n.blue{\n  color: aquamarine;\n}\n\n.mr10 {\n  margin-right: 10px;\n}\n.table-td-thumb {\n  display: block;\n  margin: auto;\n  width: 40px;\n  height: 40px;\n}\n\n</style>\n"
  },
  {
    "path": "detection-fontend/src/viewDetect/detectVue.vue",
    "content": "<template>\n  <div class=\"container\">\n    <div class=\"content-title\">模型本地上传</div>\n    <div class=\"plugins-tips\">\n      <!--      <a href=\"https://element-plus.org/zh-CN/component/upload.html\" target=\"_blank\">Element Plus Upload</a>-->\n      <p style=\"line-height: 30px\">\n        建议上传的文件格式为ultralytics可兼容的，文件名中途是不可修改的，除非删除再上传一次\n      </p>\n\n      <p style=\"line-height: 30px\">\n        首先先上传对应的pt文件,然后进行缓存,最后将填写的信息提交到终端\n      </p>\n\n      <p style=\"line-height: 30px\">\n        如果重复提交多次到缓存里,则最新的一次会覆盖掉之前的\n      </p>\n\n      <p style=\"line-height: 30px\">\n        注意填写模型名称的时候不要于现有的模型名称重复\n      </p>\n    </div>\n    <div class=\"form-box\">\n\n      <el-form ref=\"formRef\" :model=\"AddModelForm\" label-width=\"80px\" :rules=\"rules\">\n        <el-form-item label=\"模型名称\" prop=\"model_name\" label-width=\"150px\">\n          <el-input v-model=\"AddModelForm.model_name\"></el-input>\n        </el-form-item>\n\n        <el-form-item label=\"模型种类\" prop=\"model_type\" label-width=\"150px\">\n          <el-select v-model=\"AddModelForm.model_type\" placeholder=\"请选择\">\n            <el-option key=\"yolov5\" label=\"yolov5\" value=\"yolov5\"></el-option>\n            <el-option key=\"yolov8\" label=\"yolov8\" value=\"yolov8\"></el-option>\n          </el-select>\n        </el-form-item>\n\n        <el-form-item label=\"对应数据集名称\" prop=\"model_dataset\" label-width=\"150px\">\n          <el-input v-model=\"AddModelForm.model_dataset\"></el-input>\n        </el-form-item>\n\n\n        <el-form-item label=\"上传文件\" label-width=\"150px\">\n          <el-upload\n              class=\"upload-demo\"\n              drag\n              :limit=\"1\"\n              :show-file-list=\"true\"\n              action=\"http://localhost:5000/upload/model/\"\n              multiple\n              :on-success=\"handleUploadCache\"\n          >\n            <!--   action是将图片file文件直接上传到后端服务器上，要想将图片附带的数据信息上传必须要实现额外函数 -->\n            <el-icon class=\"el-icon--upload\"><upload-filled /></el-icon>\n            <div class=\"el-upload__text\">\n              将文件拖到此处，或\n              <em>点击上传</em>\n            </div>\n          </el-upload>\n        </el-form-item>\n\n        <el-form-item label-width=\"150px\" label=\"操作\">\n          <el-button type=\"primary\" @click=\"handleModelPost(AddModelForm)\">表单提交</el-button>\n          <el-button type=\"warning\" @click=\"handleModelFormReset\">重置表单</el-button>\n        </el-form-item>\n\n      </el-form>\n    </div>\n\n    <div>\n      <div class=\"content-title\">当前模型</div>\n\n      <el-descriptions :column=\"3\" border title=\"检测模型\">\n        <el-descriptions-item\n            label=\"模型ID\"\n            label-align=\"right\"\n            align=\"center\"\n            label-class-name=\"my-label\"\n            class-name=\"my-content\"\n            width=\"150px\">\n          {{model_params.wid}}\n        </el-descriptions-item>\n\n        <el-descriptions-item label=\"文件名\" label-align=\"right\" align=\"center\">\n          {{model_params.model_name}}\n        </el-descriptions-item>\n        <el-descriptions-item label=\"模型类别\" label-align=\"right\" align=\"center\">\n          <el-tag size=\"middle\" type=\"primary\">{{model_params.model_type}}</el-tag>\n        </el-descriptions-item>\n\n        <el-descriptions-item label=\"对应数据集\" label-align=\"right\" align=\"center\">\n          {{model_params.model_dataset}}\n        </el-descriptions-item>\n      </el-descriptions>\n\n    </div>\n\n    <div>\n      <div class=\"content-title\">模型管理</div>\n      <div class=\"handle-box\">\n        <el-input v-model=\"model_query.keyword\" placeholder=\"模型名称\" class=\"handle-input mr10\"></el-input>\n        <el-button type=\"primary\" :icon=\"Search \" @click=\"handleModelSearch\">搜索</el-button>\n\n        <el-button type=\"success\"  @click=\"handleModelAll\">\n          <el-icon><List /></el-icon>\n          <span>列出模型</span>\n        </el-button>\n      </div>\n\n\n      <el-table :data=\"modelData\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n        <el-table-column prop=\"wid\" label=\"模型ID\" width=\"55\" align=\"center\"></el-table-column>\n        <el-table-column prop=\"model_name\" label=\"文件名\"></el-table-column>\n\n\n        <el-table-column prop=\"model_type\" label=\"模型类别\"></el-table-column>\n        <el-table-column prop=\"model_dataset\" label=\"对应数据集\"></el-table-column>\n\n\n        <el-table-column label=\"操作\" width=\"300\" align=\"center\">\n          <template #default=\"scope\">\n<!--            <el-button text :icon=\"Search\" class=\"green\" @click=\"handleModelDetail(scope.$index, scope.row)\" v-permiss=\"15\">-->\n<!--              查看-->\n<!--            </el-button>-->\n            <el-button text :icon=\"TopLeft\" class=\"primary\" @click=\"handleModelChoose(scope.$index, scope.row)\" v-permiss=\"15\">\n              选中\n            </el-button>\n            <el-button text :icon=\"Edit\" @click=\"handleModelEdit(scope.$index, scope.row)\" v-permiss=\"15\">\n              编辑\n            </el-button>\n            <el-button text :icon=\"Delete\" class=\"red\" @click=\"handleModelDelete(scope.row)\" v-permiss=\"16\">\n              删除\n            </el-button>\n          </template>\n        </el-table-column>\n      </el-table>\n\n      <div class=\"pagination\">\n        <el-pagination\n            background\n            layout=\"total, sizes, prev, pager, next, jumper\"\n            v-model:current-page=\"model_query.curPage\"\n            v-model:page-size=\"model_query.pageSize\"\n\n            :page-sizes=\"[5, 10]\"\n            :total=\"modelTotal\"\n            @current-change=\"handleModelPageChange\"\n            @size-change=\"handleModelSizeChange\"\n\n        ></el-pagination>\n      </div>\n    </div>\n\n\n      <div class=\"content-title\">图片检测</div>\n\n      <div class=\"plugins-tips\">\n        <!--      <a href=\"https://element-plus.org/zh-CN/component/upload.html\" target=\"_blank\">Element Plus Upload</a>-->\n        <p style=\"line-height: 20px\">\n          点击检测即可得到该图片在当前模型的检测结果，但需要等待几秒钟\n        </p>\n      </div>\n\n      <div class=\"handle-box\">\n        <el-input v-model=\"query.keyword\" placeholder=\"文件名称\" class=\"handle-input mr10\"></el-input>\n        <el-button type=\"primary\" :icon=\"Search \" @click=\"handleSearch\">搜索</el-button>\n\n<!--        <el-button type=\"primary\"  @click=\"getData\">-->\n<!--          <el-icon><Refresh /></el-icon>-->\n<!--          <span>刷新页面</span>-->\n<!--        </el-button>-->\n\n        <el-button type=\"success\"  @click=\"handleAll\">\n          <el-icon><List /></el-icon>\n          <span>列出全部</span>\n        </el-button>\n      </div>\n\n      <el-table :data=\"tableData\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n        <el-table-column prop=\"fid\" label=\"文件ID\" width=\"55\" align=\"center\"></el-table-column>\n        <el-table-column prop=\"name\" label=\"文件名\"></el-table-column>\n\n        <el-table-column label=\"图片(查看大图)\" align=\"center\">\n          <template #default=\"scope\">\n            <el-image\n                class=\"table-td-thumb\"\n                :src=\"scope.row.img_url\"\n                :z-index=\"10\"\n                :preview-src-list=\"[scope.row.img_url]\"\n                preview-teleported\n            >\n            </el-image>\n          </template>\n        </el-table-column>\n\n        <el-table-column label=\"时间\">\n          <template #default=\"scope\">{{ scope.row.timestamp }}</template>\n        </el-table-column>\n\n        <el-table-column prop=\"type\" label=\"文件类别\"></el-table-column>\n        <el-table-column prop=\"origin\" label=\"文件来源\"></el-table-column>\n        <el-table-column prop=\"width\" label=\"宽度\"></el-table-column>\n        <el-table-column prop=\"height\" label=\"高度\"></el-table-column>\n\n        <!--      <el-table-column prop=\"date\" label=\"注册时间\"></el-table-column>-->\n        <el-table-column label=\"操作\" width=\"220\" align=\"center\">\n          <template #default=\"scope\">\n            <el-button text :icon=\"Search\" class=\"green\" @click=\"detection(scope.$index, scope.row)\" v-permiss=\"16\">\n              检测\n            </el-button>\n          </template>\n        </el-table-column>\n      </el-table>\n\n      <div class=\"pagination\">\n        <el-pagination\n            background\n            layout=\"total, sizes, prev, pager, next, jumper\"\n            v-model:current-page=\"query.curPage\"\n            v-model:page-size=\"query.pageSize\"\n\n            :page-sizes=\"[5, 10]\"\n            :total=\"pageTotal\"\n            @current-change=\"handlePageChange\"\n            @size-change=\"handleSizeChange\"\n\n        ></el-pagination>\n      </div>\n\n      <el-dialog title=\"编辑\" v-model=\"editVisible\" width=\"30%\">\n        <el-form label-width=\"100px\">\n<!--          <el-form-item label=\"用户名\">-->\n<!--            <el-input v-model=\"form.name\"></el-input>-->\n<!--          </el-form-item>-->\n<!--          <el-form-item label=\"地址\">-->\n<!--            <el-input v-model=\"form.address\"></el-input>-->\n<!--          </el-form-item>-->\n          <el-form-item label=\"模型ID\">\n            <span>{{form.wid}}</span>\n          </el-form-item>\n\n          <el-form-item label=\"文件名\">\n            <span>{{form.mdoel_name}}</span>\n          </el-form-item>\n\n          <el-form-item label=\"模型种类\" prop=\"region\">\n            <el-select v-model=\"form.model_type\" placeholder=\"请选择\">\n              <el-option key=\"yolov5\" label=\"yolov5\" value=\"yolov5\"></el-option>\n              <el-option key=\"yolov8\" label=\"yolov8\" value=\"yolov8\"></el-option>\n            </el-select>\n          </el-form-item>\n\n          <el-form-item label=\"对应数据集\">\n            <el-input v-model=\"form.model_dataset\"></el-input>\n          </el-form-item>\n        </el-form>\n        <template #footer>\n          <span class=\"dialog-footer\">\n            <el-button @click=\"editVisible = false\">取 消</el-button>\n            <el-button type=\"primary\" @click=\"saveModelEdit\">确 定</el-button>\n          </span>\n        </template>\n      </el-dialog>\n\n\n      <el-dialog title=\"模型细节\" v-model=\"detailVisible\" width=\"30%\">\n      <el-form label-width=\"100px\">\n        <!--          <el-form-item label=\"用户名\">-->\n        <!--            <el-input v-model=\"form.name\"></el-input>-->\n        <!--          </el-form-item>-->\n        <!--          <el-form-item label=\"地址\">-->\n        <!--            <el-input v-model=\"form.address\"></el-input>-->\n        <!--          </el-form-item>-->\n\n\n      </el-form>\n      <template #footer>\n          <span class=\"dialog-footer\">\n            <el-button @click=\"detailVisible = false\" >退出</el-button>\n          </span>\n      </template>\n    </el-dialog>\n  </div>\n\n</template>\n\n<script lang=\"ts\" setup>\nimport {ref, reactive, onMounted} from 'vue';\nimport {ElMessage, ElMessageBox, FormRules} from 'element-plus';\nimport {Delete, Edit, Search, Plus, TopLeft} from '@element-plus/icons-vue';\nimport axios from \"axios\";\nimport {compileScript} from \"@vue/compiler-sfc\";\n// import { fetchData } from '../api/index';\n\ninterface TableItem { // 定义图片的接口\n\n  // id: number;\n  // name: string;\n  // money: string;\n  // state: string;\n  // date: string;\n  // address: string;\n  fid: number;\n  height: number;\n  width: number;\n  origin: string;\n  timestamp:string;\n  type: string;\n  name: string;\n  img_url: string;\n}\n\ninterface ModelItem{ // 定义model的接口\n  wid: number,\n  model_name: string,\n  model_type: string,\n  model_dataset: string\n}\n\n// 下面定义的是file表相关的数据\nconst query = reactive({\n  curPage: 1,\n  pageSize: 10,\n  tableName: \"file\",\n  keyword: \"\"\n});\n\nconst tableData = ref<TableItem[]>([]);  // 表格数据\n\nconst pageTotal = ref(0);\n\n// 下面定义的是model表相关的数据\nconst model_query = reactive({\n  curPage: 1,\n  pageSize: 5,\n  tableName: \"model\",\n  keyword: \"\"\n})\n\nconst modelData = ref<ModelItem[]>([])\nconst modelTotal = ref(0)\n\n// 下面是定义当前后端使用那个model的表相关数据信息\nconst model_params = reactive({\n  \"model_dataset\": \"null\",\n  \"model_name\": \"null\",\n  \"model_type\": \"null\",\n  \"wid\": -1\n})\n\nconst getCurModel = () => { // 获取后端当前使用的模型信息\n  axios({\n    method: 'GET',\n    url: '/current',\n  }).then(res=>{\n    // console.log(res)\n    model_params.model_dataset =  res.data.results.model_dataset\n    model_params.model_name = res.data.results.model_name\n    model_params.model_type = res.data.results.model_type\n    model_params.wid = res.data.results.wid\n    // console.log(model_params)\n  })\n}\n\n// 获取file表格数据\nconst getData = () => {\n  // console.log(query)\n\n  if(query.keyword.length == 0){\n    axios({\n      method: 'GET',\n      url: '/page',\n      params: {\n        curPage: query.curPage,\n        pageSize: query.pageSize,\n        tableName: \"file\"\n      },\n    }).then(res=>{\n      tableData.value= res.data.results\n      // console.log(tableData)\n    })\n\n    axios({\n      method: 'GET',\n      url: '/num',\n      params: {\n        tableName: \"file\",\n        rid: -2\n      },\n    }).then(res=>{\n      pageTotal.value= res.data.results\n    })\n  }\n  else{\n    axios({\n      method: 'GET',\n      url: '/deblurS',\n      params: {\n        curPage: query.curPage,\n        pageSize: query.pageSize,\n        keyword: query.keyword,\n        tableName: \"file\"\n      },\n    }).then(res=>{\n      // console.log(res)\n      tableData.value= res.data.results\n      // console.log(tableData)\n    })\n\n    axios({\n      method: 'GET',\n      url: '/deblurSNum',\n      params: {\n        keyword: query.keyword,\n        tableName: \"file\"\n      },\n    }).then(res=>{\n      pageTotal.value= res.data.results\n    })\n  }\n};\n\n// 获得model表格的数据\nconst getModelData = () => {\n  if(model_query.keyword.length == 0){\n    axios({\n      method: 'GET',\n      url: '/page',\n      params: {\n        curPage: model_query.curPage,\n        pageSize: model_query.pageSize,\n        tableName: \"model\"\n      },\n    }).then(res=>{\n      modelData.value= res.data.results\n      // console.log(tableData)\n    })\n\n    axios({\n      method: 'GET',\n      url: '/num',\n      params: {\n        tableName: \"model\",\n        rid: -2\n      },\n    }).then(res=>{\n      modelTotal.value= res.data.results\n    })\n  }\n  else{\n    axios({\n      method: 'GET',\n      url: '/deblurS',\n      params: {\n        curPage: model_query.curPage,\n        pageSize: model_query.pageSize,\n        keyword: model_query.keyword,\n        tableName: \"model\"\n      },\n    }).then(res=>{\n      // console.log(res)\n      modelData.value= res.data.results\n      // console.log(tableData)\n    })\n\n    axios({\n      method: 'GET',\n      url: '/deblurSNum',\n      params: {\n        keyword: model_query.keyword,\n        tableName: \"model\"\n      },\n    }).then(res=>{\n      modelTotal.value= res.data.results\n    })\n  }\n}\n\n\nonMounted(() => {\n  getData()\n  getModelData()\n  getCurModel()\n})\n\n// 下面的分页get得到数据操作是针对的file表\n// 模糊查找操作\nconst handleSearch = () => {\n  query.curPage = 1;\n  getData();\n};\n// 列出全部\nconst handleAll = () => {\n  query.keyword = '';\n  query.curPage = 1\n  getData();\n}\n\n// 分页导航\nconst handlePageChange = (val: number) => {\n  query.curPage = val;\n  getData();\n};\n\nconst handleSizeChange  = (val: number) => {\n  query.pageSize = val;\n  getData();\n};\n\n// 下面的分页get得到数据操作是针对的model表\n// 模糊查找操作\nconst handleModelSearch = () => {\n  model_query.curPage = 1;\n  getModelData();\n};\n\n// 列出全部\nconst handleModelAll = () => {\n  model_query.keyword = '';\n  model_query.curPage = 1\n  getModelData();\n}\n\n// 分页导航\nconst handleModelPageChange = (val: number) => {\n  model_query.curPage = val;\n  getModelData();\n};\n\nconst handleModelSizeChange  = (val: number) => {\n  model_query.pageSize = val;\n  getModelData();\n};\n\nconst handleUploadCache = (response: any) =>{\n  if(response.status == 'success'){\n    ElMessage.success('上传暂缓PT文件成功')\n  }\n  else{\n    ElMessage.error('上传暂缓文件PT文件失败')\n  }\n}\n\n/***\n 模型相关操作\n ***/\n\n//模型表格编辑时弹窗和保存\nconst editVisible = ref(false);\nlet form = reactive({\n  wid: -1,\n  mdoel_name: '',\n  model_type: 'yolov8',\n  model_dataset: '',\n});\n\nlet idx: number = -1;\nconst handleModelEdit = (index: number, row: any) => {\n  idx = index;\n  form.model_type = row.model_type;\n  form.model_dataset = row.model_dataset\n  form.mdoel_name = row.model_name\n  form.wid = row.wid;\n  editVisible.value = true;\n};\n\nconst saveModelEdit = () => {\n  axios({\n    method: 'PUT',\n    url: '/wmodel/' + form.wid,\n    data: {\n      'model_name': form.mdoel_name,\n      'model_type': form.model_type,\n      'model_dataset': form.model_dataset\n    },\n  }).then(res=>{\n    if(res.data.status == 'success'){\n      editVisible.value = false\n      alert(\"模型信息修改成功\")\n      getModelData()\n    }\n    // console.log(tableData)\n  })\n}\n\nconst handleModelChoose = (index: number, row: any) => {\n    axios(\n        {\n          method: 'PUT',\n          url: '/switch',\n          data: row\n        }\n    ).then(res=>{\n      if(res.data.status == 'success'){\n        ElMessage.success('切换模型成功');\n        getCurModel()\n      }\n    })\n}\n\n//上传添加新模型相关操作\nconst AddModelForm = reactive({\n  model_name: '',\n  model_type: 'yolov8',\n  model_dataset: ''\n})\n\n// 查看模型的内部细节\nconst detailVisible = ref(false)\n\nconst handleModelDetail = (index: number, row: any) =>{\n  detailVisible.value = true\n  // console.log(row)\n  axios({\n    method: 'GET',\n    url: '/detail/model',\n    params: row\n  }).then(res => {\n    console.log(res)\n    if(res.data.status == 'success'){\n\n    }\n  })\n}\n\n// 上传模型相关信息\n\nconst handleModelPost = (form: any) => {\n  // console.log(\"114514\")\n  // console.log(form)\n  if(form.model_dataset.length == 0 || form.model_name.length < 3){\n    ElMessage.error('提交失败,请检查是否符合表格要求')\n  }\n  else{\n    axios({\n      method: 'POST',\n      url: '/wmodel', // 这里的后端请求url功能和文件复制是耦合的，也就是 复制pt -> 创建pt信息\n      data: {\n        'model_name': form.model_name,\n        'model_dataset': form.model_dataset,\n        'model_type': form.model_type\n      }\n    }).then(res => {\n      if(res.data.status == 'success'){\n        ElMessage.success('模型上传成功')\n        getModelData()\n      }\n      else{\n        ElMessage.error('模型上传失败')\n      }\n    })\n  }\n}\n\nconst handleModelFormReset = () => {\n  AddModelForm.model_name = ''\n  AddModelForm.model_type = 'yolov8'\n  AddModelForm.model_dataset = ''\n}\n\nconst rules = reactive<FormRules>({\n  model_name: [\n    {required: true, message: '请输入模型名称', trigger: 'blur' },\n    {min: 3, message: '长度至少为3', trigger: 'blur'}\n  ],\n  model_dataset:[\n    { required: true, message: '请输入数据集名称', trigger: 'blur' },\n  ]\n})\n\n// 删除操作\nconst handleModelDelete = (scope: any) => {\n  // console.log(scope)\n  if(scope.wid <= 6){\n    ElMessage.error('系统不允许删除前六个固定模型')\n  }\n  else{\n\n    if(scope.wid != model_params.wid){\n      ElMessageBox.confirm('确定要删除吗？', '提示', {\n        type: 'warning'\n      }).then(()=>{\n        axios({\n          method: 'delete',\n          url: '/wmodel/' + scope.wid  // 这里的后端请求url功能和文件删除是耦合的，也就是 删除pt -> 删除pt信息\n        }).then(res => {\n          if(res.data.status == 'success'){\n            ElMessage.success('删除模型成功')\n            getModelData()\n          }else{\n            ElMessage.error('删除模型失败')\n          }\n        })\n      })\n    }\n    else{\n      ElMessage.error('删除的模型是当前后端所使用的模型')\n    }\n  }\n}\n\n\n/**\n * 实现图片检测相关操作\n */\nconst detection = (index: number, row: any) => {\n  console.log(row)\n  axios({ // 首先执行检测任务，生成结果图片\n    method: 'POST',\n    url: '/detect',\n    data: row\n  }).then(res1=>{\n    // console.log(res1)\n    if(res1.data.status == 'success'){ // 然后再把result得到的json传入到数据库中\n      axios({\n        method: 'POST',\n        url: '/result',\n        data: res1.data.results.log\n      }).then(res2=>{\n        // console.log(res2)\n        if(res2.data.status == 'success'){ // 最后再把obj信息传入到obj中\n          axios({\n            method: 'POST',\n            url: '/obj',\n            data: res1.data // 在/detect后端哪里得到的json信息\n          }).then(res3 => {\n            // console.log(res3)\n              if(res3.data.status == 'success'){\n                ElMessage.success('目标图片检测成功，保存的文件名称是:\\n'\n                    + res1.data.results.log.filename + '_' +\n                    res1.data.results.log.timestamp + '.' + res1.data.results.log.type);\n              }\n          })\n        }\n      })\n    }\n    else{\n      ElMessage.error('检测中途出现异常')\n    }\n  })\n}\n\n</script>\n\n<style scoped>\n.content-title {\n  font-weight: 400;\n  line-height: 50px;\n  margin: 10px 0;\n  font-size: 22px;\n  color: #1f2f3d;\n}\n.upload-demo {\n  width: 360px;\n}\n\n.handle-box {\n  margin-bottom: 20px;\n}\n\n.handle-select {\n  width: 120px;\n}\n\n.handle-input {\n  width: 300px;\n}\n.table {\n  width: 100%;\n  font-size: 14px;\n}\n.red {\n  color: red;\n}\n\n.primary{\n  color: cornflowerblue;\n}\n\n.green{\n  color: green;\n}\n\n.blue{\n  color: aquamarine;\n}\n\n.mr10 {\n  margin-right: 10px;\n}\n.table-td-thumb {\n  display: block;\n  margin: auto;\n  width: 40px;\n  height: 40px;\n}\n\n.my-label {\n  background: var(--el-color-success-light-9);\n}\n.my-content {\n  background: var(--el-color-danger-light-9);\n}\n\n</style>\n"
  },
  {
    "path": "detection-fontend/src/viewDetect/uploadFile.vue",
    "content": "<template>\n  <div class=\"container\">\n\n    <div>\n      <div class=\"content-title\">文件本地上传</div>\n      <div class=\"plugins-tips\">\n        <!--      <a href=\"https://element-plus.org/zh-CN/component/upload.html\" target=\"_blank\">Element Plus Upload</a>-->\n        <p style=\"line-height: 20px\">\n          建议上传的图片格式为JPG & PNG\n        </p>\n      </div>\n\n      <el-upload\n          class=\"upload-demo\"\n          drag\n          :limit=\"1\"\n          :show-file-list=\"true\"\n          action=\"http://localhost:5000/upload/pic/\"\n          multiple\n          :on-success=\"upload\"\n      >\n        <!--   action是将图片file文件直接上传到后端服务器上，要想将图片附带的数据信息上传必须要实现额外函数 -->\n        <el-icon class=\"el-icon--upload\"><upload-filled /></el-icon>\n        <div class=\"el-upload__text\">\n          将文件拖到此处，或\n          <em>点击上传</em>\n        </div>\n      </el-upload>\n    </div>\n\n\n    <div>\n      <div class=\"content-title\">URL地址上传</div>\n\n      <div class=\"plugins-tips\">\n        <!--      <a href=\"https://element-plus.org/zh-CN/component/upload.html\" target=\"_blank\">Element Plus Upload</a>-->\n        <p style=\"line-height: 30px\">\n          参照的格式如下:\n        </p>\n        <p style=\"line-height: 30px\">\n          https://n.sinaimg.cn/sinakd20220516s/290/w1080h810/20220516/8114-e6ab336af96e393f3f312a58349114d8.png\n        </p>\n      </div>\n\n      <el-input v-model=\"url\" placeholder=\"图片url地址\" class=\"handle-input mr10\"></el-input>\n\n      <el-button type=\"primary\"  @click=\"uploadURL(url)\">\n        <el-icon><Upload /></el-icon>\n        <span>上传</span>\n      </el-button>\n    </div>\n\n    <div class=\"content-title\">图片查询</div>\n    <div class=\"handle-box\">\n      <el-input v-model=\"query.keyword\" placeholder=\"文件名称\" class=\"handle-input mr10\"></el-input>\n      <el-button type=\"primary\" :icon=\"Search \" @click=\"handleSearch\">搜索</el-button>\n\n      <el-button type=\"success\"  @click=\"handleAll\">\n        <el-icon><List /></el-icon>\n        <span>列出全部</span>\n      </el-button>\n    </div>\n\n    <el-table :data=\"tableData\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n      <el-table-column prop=\"fid\" label=\"文件ID\" width=\"55\" align=\"center\"></el-table-column>\n      <el-table-column prop=\"name\" label=\"文件名\"></el-table-column>\n\n      <el-table-column label=\"图片(查看大图)\" align=\"center\">\n        <template #default=\"scope\">\n          <el-image\n              class=\"table-td-thumb\"\n              :src=\"scope.row.img_url\"\n              :z-index=\"10\"\n              :preview-src-list=\"[scope.row.img_url]\"\n              preview-teleported\n          >\n          </el-image>\n        </template>\n      </el-table-column>\n\n      <el-table-column label=\"时间\">\n        <template #default=\"scope\">{{ scope.row.timestamp }}</template>\n      </el-table-column>\n\n      <el-table-column prop=\"type\" label=\"文件类别\"></el-table-column>\n      <el-table-column prop=\"origin\" label=\"文件来源\"></el-table-column>\n      <el-table-column prop=\"width\" label=\"宽度\"></el-table-column>\n      <el-table-column prop=\"height\" label=\"高度\"></el-table-column>\n<!--      <el-table-column prop=\"date\" label=\"注册时间\"></el-table-column>-->\n      <el-table-column label=\"操作\" width=\"220\" align=\"center\">\n        <template #default=\"scope\">\n          <el-button text :icon=\"Delete\" class=\"red\" @click=\"handleDelete(scope.$index)\" v-permiss=\"16\">\n            删除\n          </el-button>\n<!--          <el-button text @click=\"deletePrepare\">Click to open the Message Box</el-button>-->\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <div class=\"pagination\">\n      <el-pagination\n          background\n          layout=\"total, sizes, prev, pager, next, jumper\"\n          v-model:current-page=\"query.curPage\"\n          v-model:page-size=\"query.pageSize\"\n\n          :page-sizes=\"[5, 10, 20]\"\n          :total=\"pageTotal\"\n          @current-change=\"handlePageChange\"\n          @size-change=\"handleSizeChange\"\n\n      ></el-pagination>\n    </div>\n\n  </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport {ref, reactive, onMounted} from 'vue';\nimport { ElMessage, ElMessageBox } from 'element-plus';\nimport { Delete, Edit, Search, Plus } from '@element-plus/icons-vue';\nimport axios from \"axios\";\nimport {compileScript} from \"@vue/compiler-sfc\";\n// import { fetchData } from '../api/index';\n\ninterface TableItem {\n\n  // id: number;\n  // name: string;\n  // money: string;\n  // state: string;\n  // date: string;\n  // address: string;\n  fid: number;\n  height: number;\n  width: number;\n  origin: string;\n  timestamp:string;\n  type: string;\n  name: string;\n  img_url: string;\n}\n\nconst query = reactive({\n  curPage: 1,\n  pageSize: 10,\n  tableName: \"file\",\n  keyword: \"\"\n});\n\nconst tableData = ref<TableItem[]>([]);  // 表格数据\n\nconst pageTotal = ref(0);\n\nconst url = ref(\"\")\n// 获取表格数据\nconst getData = () => {\n  // console.log(query)\n\n  if(query.keyword.length == 0){\n      axios({\n        method: 'GET',\n        url: '/page',\n        params: {\n          curPage: query.curPage,\n          pageSize: query.pageSize,\n          tableName: \"file\"\n        },\n      }).then(res=>{\n        tableData.value= res.data.results\n        // console.log(tableData)\n      })\n\n      axios({\n        method: 'GET',\n        url: '/num',\n        params: {\n          tableName: \"file\",\n          rid: -2\n        },\n      }).then(res=>{\n        pageTotal.value= res.data.results\n      })\n  }\n  else{\n    axios({\n      method: 'GET',\n      url: '/deblurS',\n      params: {\n        curPage: query.curPage,\n        pageSize: query.pageSize,\n        keyword: query.keyword,\n        tableName: \"file\"\n      },\n    }).then(res=>{\n      // console.log(res)\n      tableData.value= res.data.results\n      // console.log(tableData)\n    })\n\n    axios({\n      method: 'GET',\n      url: '/deblurSNum',\n      params: {\n        keyword: query.keyword,\n        tableName: \"file\"\n      },\n    }).then(res=>{\n      pageTotal.value= res.data.results\n    })\n  }\n};\n\n\nonMounted(() => {\n  getData()\n  // getNum()\n})\n\n// 查询操作\nconst handleSearch = () => {\n  query.curPage = 1;\n  getData();\n};\n// 列出全部\nconst handleAll = () => {\n  query.keyword = '';\n  query.curPage = 1\n  getData();\n}\n\n// 分页导航\nconst handlePageChange = (val: number) => {\n  query.curPage = val;\n  getData();\n};\n\nconst handleSizeChange  = (val: number) => {\n  query.pageSize = val;\n  getData();\n};\n\n// 删除操作\nconst deletePrepare = (index: number) => {\n\n}\nconst handleDelete = (index: number) => {\n  // 二次确认删除\n  ElMessageBox.confirm('确定要删除吗？', '提示', {\n    type: 'warning'\n  })\n      .then(() => {\n        var _cache: any = tableData.value[index].fid\n        // console.log(tableData.value[index])\n        axios.delete(\"/delete/file\", {\n          data:{\n            type: tableData.value[index].type,\n            name: tableData.value[index].name\n          }\n        }).then(\n            res=>{\n              // console.log(res.data.status)\n              if(res.data.status == 'success'){\n                // console.log(\"1919\")\n                axios.delete(\"/files/\"+ _cache).then(\n                    res2=>{\n                      // console.log(res2)\n                      if(res2.data.status == 'success'){\n                        ElMessage.success('删除成功');\n                        tableData.value.splice(index, 1);\n                        // alert(\"删除图片成功\")\n                        getData()\n                      }\n                    }\n                )\n              }\n            }\n        )\n      }\n      ).catch(() => {ElMessage.success('删除图片过程中断');});\n  // getData()\n};\n\n// 本地文件上传相关操作\nconst upload = (response: any, file: any, filelist: any) => {\n  // console.log('114514')\n  // console.log(response)\n  // console.log(\"114514\")\n  // console.log({\n  //   width: response.results.width,\n  //   height: response.results.height,\n  //   origin: response.results.origin,\n  //   type: response.results.type,\n  //   name: response.results.name,\n  // })\n    axios.post(\n        '/files', {\n          width: response.results.width,\n          height: response.results.height,\n          origin: response.results.origin,\n          type: response.results.type,\n          name: response.results.name,\n        }\n    ).then(res=>{\n      if(res.data.status == 'success'){\n        // console.log(1919810)\n        getData()\n      }\n    })\n};\n\n\n// URL图片上传相关操作\nconst uploadURL = (url: any) => {\n  // console.log(url)\n  // console.log(\"114514\")\n  axios.post('/upload/url', {\n    url: url\n  }).then((res)=>{\n    if(res.data.status == 'success'){\n      axios.post('/files', res.data.results).then(\n          (res) => {\n            if(res.data.status == 'success'){\n              alert('url添加数据成功')\n              getData()\n            }\n            else{\n              alert('出现异常')\n            }\n          }\n      )\n    }\n    else{\n      alert(\"爬取图片失败\")\n    }\n  })\n}\n</script>\n\n<style scoped>\n.content-title {\n  font-weight: 400;\n  line-height: 50px;\n  margin: 10px 0;\n  font-size: 22px;\n  color: #1f2f3d;\n}\n.upload-demo {\n  width: 360px;\n}\n\n.handle-box {\n  margin-bottom: 20px;\n}\n\n.handle-select {\n  width: 120px;\n}\n\n.handle-input {\n  width: 300px;\n}\n.table {\n  width: 100%;\n  font-size: 14px;\n}\n.red {\n  color: #F56C6C;\n}\n.mr10 {\n  margin-right: 10px;\n}\n.table-td-thumb {\n  display: block;\n  margin: auto;\n  width: 40px;\n  height: 40px;\n}\n\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/403.vue",
    "content": "<template>\n\t<div class=\"error-page\">\n\t\t<div class=\"error-code\">4<span>0</span>3</div>\n\t\t<div class=\"error-desc\">啊哦~ 你没有权限访问该页面哦</div>\n\t\t<div class=\"error-handle\">\n\t\t\t<router-link to=\"/\">\n\t\t\t\t<el-button type=\"primary\" size=\"large\">返回首页</el-button>\n\t\t\t</router-link>\n\t\t\t<el-button class=\"error-btn\" type=\"primary\" size=\"large\" @click=\"goBack\">返回上一页</el-button>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"403\">\nimport { useRouter } from 'vue-router';\n\nconst router = useRouter();\nconst goBack = () => {\n\trouter.go(-2);\n};\n</script>\n\n<style scoped>\n.error-page {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\tflex-direction: column;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: #f3f3f3;\n\tbox-sizing: border-box;\n}\n.error-code {\n\tline-height: 1;\n\tfont-size: 250px;\n\tfont-weight: bolder;\n\tcolor: #f02d2d;\n}\n.error-code span {\n\tcolor: #00a854;\n}\n.error-desc {\n\tfont-size: 30px;\n\tcolor: #777;\n}\n.error-handle {\n\tmargin-top: 30px;\n\tpadding-bottom: 200px;\n}\n.error-btn {\n\tmargin-left: 100px;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/404.vue",
    "content": "<template>\n\t<div class=\"error-page\">\n\t\t<div class=\"error-code\">4<span>0</span>4</div>\n\t\t<div class=\"error-desc\">啊哦~ 你所访问的页面不存在</div>\n\t\t<div class=\"error-handle\">\n\t\t\t<router-link to=\"/\">\n\t\t\t\t<el-button type=\"primary\" size=\"large\">返回首页</el-button>\n\t\t\t</router-link>\n\t\t\t<el-button class=\"error-btn\" type=\"primary\" size=\"large\" @click=\"goBack\">返回上一页</el-button>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"404\">\nimport { useRouter } from 'vue-router';\n\nconst router = useRouter();\nconst goBack = () => {\n\trouter.go(-1);\n};\n</script>\n\n<style scoped>\n.error-page {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\tflex-direction: column;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: #f3f3f3;\n\tbox-sizing: border-box;\n}\n.error-code {\n\tline-height: 1;\n\tfont-size: 250px;\n\tfont-weight: bolder;\n\tcolor: #2d8cf0;\n}\n.error-code span {\n\tcolor: #00a854;\n}\n.error-desc {\n\tfont-size: 30px;\n\tcolor: #777;\n}\n.error-handle {\n\tmargin-top: 30px;\n\tpadding-bottom: 200px;\n}\n.error-btn {\n\tmargin-left: 100px;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/charts.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<div class=\"plugins-tips\">\n\t\t\tvue-schart：vue.js封装sChart.js的图表组件。 访问地址：\n\t\t\t<a href=\"https://github.com/lin-xin/vue-schart\" target=\"_blank\">vue-schart</a>\n\t\t</div>\n\t\t<div class=\"schart-box\">\n\t\t\t<div class=\"content-title\">柱状图</div>\n\t\t\t<schart class=\"schart\" canvasId=\"bar\" :options=\"options1\"></schart>\n\t\t</div>\n\t\t<div class=\"schart-box\">\n\t\t\t<div class=\"content-title\">折线图</div>\n\t\t\t<schart class=\"schart\" canvasId=\"line\" :options=\"options2\"></schart>\n\t\t</div>\n\t\t<div class=\"schart-box\">\n\t\t\t<div class=\"content-title\">饼状图</div>\n\t\t\t<schart class=\"schart\" canvasId=\"pie\" :options=\"options3\"></schart>\n\t\t</div>\n\t\t<div class=\"schart-box\">\n\t\t\t<div class=\"content-title\">环形图</div>\n\t\t\t<schart class=\"schart\" canvasId=\"ring\" :options=\"options4\"></schart>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"basecharts\">\nimport Schart from 'vue-schart';\n\nconst options1 = {\n\ttype: 'bar',\n\ttitle: {\n\t\ttext: '最近一周各品类销售图'\n\t},\n\tbgColor: '#fbfbfb',\n\tlabels: ['周一', '周二', '周三', '周四', '周五'],\n\tdatasets: [\n\t\t{\n\t\t\tlabel: '家电',\n\t\t\tfillColor: 'rgba(241, 49, 74, 0.5)',\n\t\t\tdata: [234, 278, 270, 190, 230]\n\t\t},\n\t\t{\n\t\t\tlabel: '百货',\n\t\t\tdata: [164, 178, 190, 135, 160]\n\t\t},\n\t\t{\n\t\t\tlabel: '食品',\n\t\t\tdata: [144, 198, 150, 235, 120]\n\t\t}\n\t]\n};\nconst options2 = {\n\ttype: 'line',\n\ttitle: {\n\t\ttext: '最近几个月各品类销售趋势图'\n\t},\n\tbgColor: '#fbfbfb',\n\tlabels: ['6月', '7月', '8月', '9月', '10月'],\n\tdatasets: [\n\t\t{\n\t\t\tlabel: '家电',\n\t\t\tdata: [234, 278, 270, 190, 230]\n\t\t},\n\t\t{\n\t\t\tlabel: '百货',\n\t\t\tdata: [164, 178, 150, 135, 160]\n\t\t},\n\t\t{\n\t\t\tlabel: '食品',\n\t\t\tdata: [114, 138, 200, 235, 190]\n\t\t}\n\t]\n};\nconst options3 = {\n\ttype: 'pie',\n\ttitle: {\n\t\ttext: '服装品类销售饼状图'\n\t},\n\tlegend: {\n\t\tposition: 'left'\n\t},\n\tbgColor: '#fbfbfb',\n\tlabels: ['T恤', '牛仔裤', '连衣裙', '毛衣', '七分裤', '短裙', '羽绒服'],\n\tdatasets: [\n\t\t{\n\t\t\tdata: [334, 278, 190, 235, 260, 200, 141]\n\t\t}\n\t]\n};\nconst options4 = {\n\ttype: 'ring',\n\ttitle: {\n\t\ttext: '环形三等分'\n\t},\n\tshowValue: false,\n\tlegend: {\n\t\tposition: 'bottom',\n\t\tbottom: 40\n\t},\n\tbgColor: '#fbfbfb',\n\tlabels: ['vue', 'react', 'angular'],\n\tdatasets: [\n\t\t{\n\t\t\tdata: [500, 500, 500]\n\t\t}\n\t]\n};\n</script>\n\n<style scoped>\n.schart-box {\n\tdisplay: inline-block;\n\tmargin: 20px;\n}\n.schart {\n\twidth: 600px;\n\theight: 400px;\n}\n.content-title {\n\tclear: both;\n\tfont-weight: 400;\n\tline-height: 50px;\n\tmargin: 10px 0;\n\tfont-size: 22px;\n\tcolor: #1f2f3d;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/dashboard.vue",
    "content": "<template>\n\t<div>\n\t\t<el-row :gutter=\"20\">\n\t\t\t<el-col :span=\"8\">\n\t\t\t\t<el-card shadow=\"hover\" class=\"mgb20\" style=\"height: 252px\">\n\t\t\t\t\t<div class=\"user-info\">\n\t\t\t\t\t\t<el-avatar :size=\"120\" :src=\"imgurl\" />\n\t\t\t\t\t\t<div class=\"user-info-cont\">\n\t\t\t\t\t\t\t<div class=\"user-info-name\">{{ name }}</div>\n\t\t\t\t\t\t\t<div>{{ role }}</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"user-info-list\">\n\t\t\t\t\t\t上次登录时间：\n\t\t\t\t\t\t<span>2022-10-01</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"user-info-list\">\n\t\t\t\t\t\t上次登录地点：\n\t\t\t\t\t\t<span>东莞</span>\n\t\t\t\t\t</div>\n\t\t\t\t</el-card>\n\t\t\t\t<el-card shadow=\"hover\" style=\"height: 252px\">\n\t\t\t\t\t<template #header>\n\t\t\t\t\t\t<div class=\"clearfix\">\n\t\t\t\t\t\t\t<span>语言详情</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\t\t\t\t\tVue\n\t\t\t\t\t<el-progress :percentage=\"79.4\" color=\"#42b983\"></el-progress>\n\t\t\t\t\tTypeScript\n\t\t\t\t\t<el-progress :percentage=\"14\" color=\"#f1e05a\"></el-progress>\n\t\t\t\t\tCSS\n\t\t\t\t\t<el-progress :percentage=\"5.6\"></el-progress>\n\t\t\t\t\tHTML\n\t\t\t\t\t<el-progress :percentage=\"1\" color=\"#f56c6c\"></el-progress>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t\t<el-col :span=\"16\">\n\t\t\t\t<el-row :gutter=\"20\" class=\"mgb20\">\n\t\t\t\t\t<el-col :span=\"8\">\n\t\t\t\t\t\t<el-card shadow=\"hover\" :body-style=\"{ padding: '0px' }\">\n\t\t\t\t\t\t\t<div class=\"grid-content grid-con-1\">\n\t\t\t\t\t\t\t\t<el-icon class=\"grid-con-icon\"><User /></el-icon>\n\t\t\t\t\t\t\t\t<div class=\"grid-cont-right\">\n\t\t\t\t\t\t\t\t\t<div class=\"grid-num\">1234</div>\n\t\t\t\t\t\t\t\t\t<div>用户访问量</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</el-card>\n\t\t\t\t\t</el-col>\n\t\t\t\t\t<el-col :span=\"8\">\n\t\t\t\t\t\t<el-card shadow=\"hover\" :body-style=\"{ padding: '0px' }\">\n\t\t\t\t\t\t\t<div class=\"grid-content grid-con-2\">\n\t\t\t\t\t\t\t\t<el-icon class=\"grid-con-icon\"><ChatDotRound /></el-icon>\n\t\t\t\t\t\t\t\t<div class=\"grid-cont-right\">\n\t\t\t\t\t\t\t\t\t<div class=\"grid-num\">321</div>\n\t\t\t\t\t\t\t\t\t<div>系统消息</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</el-card>\n\t\t\t\t\t</el-col>\n\t\t\t\t\t<el-col :span=\"8\">\n\t\t\t\t\t\t<el-card shadow=\"hover\" :body-style=\"{ padding: '0px' }\">\n\t\t\t\t\t\t\t<div class=\"grid-content grid-con-3\">\n\t\t\t\t\t\t\t\t<el-icon class=\"grid-con-icon\"><Goods /></el-icon>\n\t\t\t\t\t\t\t\t<div class=\"grid-cont-right\">\n\t\t\t\t\t\t\t\t\t<div class=\"grid-num\">5000</div>\n\t\t\t\t\t\t\t\t\t<div>商品数量</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</el-card>\n\t\t\t\t\t</el-col>\n\t\t\t\t</el-row>\n\t\t\t\t<el-card shadow=\"hover\" style=\"height: 403px\">\n\t\t\t\t\t<template #header>\n\t\t\t\t\t\t<div class=\"clearfix\">\n\t\t\t\t\t\t\t<span>待办事项</span>\n\t\t\t\t\t\t\t<el-button style=\"float: right; padding: 3px 0\" text>添加</el-button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\n\t\t\t\t\t<el-table :show-header=\"false\" :data=\"todoList\" style=\"width: 100%\">\n\t\t\t\t\t\t<el-table-column width=\"40\">\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<el-checkbox v-model=\"scope.row.status\"></el-checkbox>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t\t<el-table-column>\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclass=\"todo-item\"\n\t\t\t\t\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t\t\t\t\t'todo-item-del': scope.row.status\n\t\t\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{{ scope.row.title }}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t</el-table>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t</el-row>\n\t\t<el-row :gutter=\"20\">\n\t\t\t<el-col :span=\"12\">\n\t\t\t\t<el-card shadow=\"hover\">\n\t\t\t\t\t<schart ref=\"bar\" class=\"schart\" canvasId=\"bar\" :options=\"options\"></schart>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t\t<el-col :span=\"12\">\n\t\t\t\t<el-card shadow=\"hover\">\n\t\t\t\t\t<schart ref=\"line\" class=\"schart\" canvasId=\"line\" :options=\"options2\"></schart>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t</el-row>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"dashboard\">\nimport Schart from 'vue-schart';\nimport { reactive } from 'vue';\nimport imgurl from '../assets/img/img.jpg';\n\nconst name = localStorage.getItem('ms_username');\nconst role: string = name === 'admin' ? '超级管理员' : '普通用户';\n\nconst options = {\n\ttype: 'bar',\n\ttitle: {\n\t\ttext: '最近一周各品类销售图'\n\t},\n\txRorate: 25,\n\tlabels: ['周一', '周二', '周三', '周四', '周五'],\n\tdatasets: [\n\t\t{\n\t\t\tlabel: '家电',\n\t\t\tdata: [234, 278, 270, 190, 230]\n\t\t},\n\t\t{\n\t\t\tlabel: '百货',\n\t\t\tdata: [164, 178, 190, 135, 160]\n\t\t},\n\t\t{\n\t\t\tlabel: '食品',\n\t\t\tdata: [144, 198, 150, 235, 120]\n\t\t}\n\t]\n};\nconst options2 = {\n\ttype: 'line',\n\ttitle: {\n\t\ttext: '最近几个月各品类销售趋势图'\n\t},\n\tlabels: ['6月', '7月', '8月', '9月', '10月'],\n\tdatasets: [\n\t\t{\n\t\t\tlabel: '家电',\n\t\t\tdata: [234, 278, 270, 190, 230]\n\t\t},\n\t\t{\n\t\t\tlabel: '百货',\n\t\t\tdata: [164, 178, 150, 135, 160]\n\t\t},\n\t\t{\n\t\t\tlabel: '食品',\n\t\t\tdata: [74, 118, 200, 235, 90]\n\t\t}\n\t]\n};\nconst todoList = reactive([\n\t{\n\t\ttitle: '今天要修复100个bug',\n\t\tstatus: false\n\t},\n\t{\n\t\ttitle: '今天要修复100个bug',\n\t\tstatus: false\n\t},\n\t{\n\t\ttitle: '今天要写100行代码加几个bug吧',\n\t\tstatus: false\n\t},\n\t{\n\t\ttitle: '今天要修复100个bug',\n\t\tstatus: false\n\t},\n\t{\n\t\ttitle: '今天要修复100个bug',\n\t\tstatus: true\n\t},\n\t{\n\t\ttitle: '今天要写100行代码加几个bug吧',\n\t\tstatus: true\n\t}\n]);\n</script>\n\n<style scoped>\n.el-row {\n\tmargin-bottom: 20px;\n}\n\n.grid-content {\n\tdisplay: flex;\n\talign-items: center;\n\theight: 100px;\n}\n\n.grid-cont-right {\n\tflex: 1;\n\ttext-align: center;\n\tfont-size: 14px;\n\tcolor: #999;\n}\n\n.grid-num {\n\tfont-size: 30px;\n\tfont-weight: bold;\n}\n\n.grid-con-icon {\n\tfont-size: 50px;\n\twidth: 100px;\n\theight: 100px;\n\ttext-align: center;\n\tline-height: 100px;\n\tcolor: #fff;\n}\n\n.grid-con-1 .grid-con-icon {\n\tbackground: rgb(45, 140, 240);\n}\n\n.grid-con-1 .grid-num {\n\tcolor: rgb(45, 140, 240);\n}\n\n.grid-con-2 .grid-con-icon {\n\tbackground: rgb(100, 213, 114);\n}\n\n.grid-con-2 .grid-num {\n\tcolor: rgb(100, 213, 114);\n}\n\n.grid-con-3 .grid-con-icon {\n\tbackground: rgb(242, 94, 67);\n}\n\n.grid-con-3 .grid-num {\n\tcolor: rgb(242, 94, 67);\n}\n\n.user-info {\n\tdisplay: flex;\n\talign-items: center;\n\tpadding-bottom: 20px;\n\tborder-bottom: 2px solid #ccc;\n\tmargin-bottom: 20px;\n}\n\n.user-info-cont {\n\tpadding-left: 50px;\n\tflex: 1;\n\tfont-size: 14px;\n\tcolor: #999;\n}\n\n.user-info-cont div:first-child {\n\tfont-size: 30px;\n\tcolor: #222;\n}\n\n.user-info-list {\n\tfont-size: 14px;\n\tcolor: #999;\n\tline-height: 25px;\n}\n\n.user-info-list span {\n\tmargin-left: 70px;\n}\n\n.mgb20 {\n\tmargin-bottom: 20px;\n}\n\n.todo-item {\n\tfont-size: 14px;\n}\n\n.todo-item-del {\n\ttext-decoration: line-through;\n\tcolor: #999;\n}\n\n.schart {\n\twidth: 100%;\n\theight: 300px;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/donate.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<div class=\"plugins-tips\">\n\t\t\t如果该框架对你有帮助，那就请作者喝杯饮料吧！<el-icon><ColdDrink /></el-icon> 加微信号linxin_20探讨问题。\n\t\t</div>\n\t\t<div>\n\t\t\t<img src=\"https://lin-xin.gitee.io/images/weixin.jpg\" />\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"donate\"></script>\n\n<style></style>\n"
  },
  {
    "path": "detection-fontend/src/views/editor.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<div class=\"plugins-tips\">\n\t\t\twangEditor：轻量级 web 富文本编辑器，配置方便，使用简单。 访问地址：\n\t\t\t<a href=\"https://www.wangeditor.com/doc/\" target=\"_blank\">wangEditor</a>\n\t\t</div>\n\t\t<div class=\"mgb20\" ref=\"editor\"></div>\n\t\t<el-button type=\"primary\" @click=\"syncHTML\">提交</el-button>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"editor\">\nimport WangEditor from 'wangeditor';\nimport { ref, reactive, onMounted, onBeforeUnmount } from 'vue';\n\nconst editor = ref(null);\nconst content = reactive({\n\thtml: '',\n\ttext: ''\n});\nlet instance: any;\nonMounted(() => {\n\tinstance = new WangEditor(editor.value);\n\tinstance.config.zIndex = 1;\n\tinstance.create();\n});\nonBeforeUnmount(() => {\n\tinstance.destroy();\n\tinstance = null;\n});\nconst syncHTML = () => {\n\tcontent.html = instance.txt.html();\n\tconsole.log(content.html);\n};\n</script>\n\n<style></style>\n"
  },
  {
    "path": "detection-fontend/src/views/export.vue",
    "content": "<template>\n    <div>\n        <div class=\"container\">\n            <div class=\"handle-box\">\n                <el-button type=\"primary\" @click=\"exportXlsx\">导出Excel</el-button>\n            </div>\n            <el-table :data=\"tableData\" border class=\"table\" header-cell-class-name=\"table-header\">\n                <el-table-column prop=\"id\" label=\"ID\" width=\"55\" align=\"center\"></el-table-column>\n                <el-table-column prop=\"name\" label=\"姓名\"></el-table-column>\n                <el-table-column prop=\"sno\" label=\"学号\"></el-table-column>\n                <el-table-column prop=\"class\" label=\"班级\"></el-table-column>\n                <el-table-column prop=\"age\" label=\"年龄\"></el-table-column>\n                <el-table-column prop=\"sex\" label=\"性别\"></el-table-column>\n            </el-table>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\" name=\"export\">\nimport { ref } from 'vue';\nimport * as XLSX from 'xlsx';\n\ninterface TableItem {\n    id: number;\n    name: string;\n    sno: string;\n    class: string;\n    age: string;\n    sex: string;\n}\n\nconst tableData = ref<TableItem[]>([]);\n// 获取表格数据\nconst getData = () => {\n    tableData.value = [\n        {\n            id: 1,\n            name: '小明',\n            sno: 'S001',\n            class: '一班',\n            age: '10',\n            sex: '男',\n        },\n        {\n            id: 2,\n            name: '小红',\n            sno: 'S002',\n            class: '一班',\n            age: '9',\n            sex: '女',\n        },\n    ];\n};\ngetData();\n\nconst list = [['序号', '姓名', '学号', '班级', '年龄', '性别']];\nconst exportXlsx = () => {\n    tableData.value.map((item: any, i: number) => {\n        const arr: any[] = [i + 1];\n        arr.push(...[item.name, item.sno, item.class, item.age, item.sex]);\n        list.push(arr);\n    });\n    let WorkSheet = XLSX.utils.aoa_to_sheet(list);\n    let new_workbook = XLSX.utils.book_new();\n    XLSX.utils.book_append_sheet(new_workbook, WorkSheet, '第一页');\n    XLSX.writeFile(new_workbook, `表格.xlsx`);\n};\n</script>\n\n<style scoped>\n.handle-box {\n    margin-bottom: 20px;\n}\n\n.handle-select {\n    width: 120px;\n}\n\n.handle-input {\n    width: 300px;\n}\n.table {\n    width: 100%;\n    font-size: 14px;\n}\n.red {\n    color: #f56c6c;\n}\n.mr10 {\n    margin-right: 10px;\n}\n.table-td-thumb {\n    display: block;\n    margin: auto;\n    width: 40px;\n    height: 40px;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/form.vue",
    "content": "<template>\n    <div class=\"container\">\n        <div class=\"form-box\">\n            <el-form ref=\"formRef\" :rules=\"rules\" :model=\"form\" label-width=\"80px\">\n                <el-form-item label=\"表单名称\" prop=\"name\">\n                    <el-input v-model=\"form.name\"></el-input>\n                </el-form-item>\n                <el-form-item label=\"选择器\" prop=\"region\">\n                    <el-select v-model=\"form.region\" placeholder=\"请选择\">\n                        <el-option key=\"小明\" label=\"小明\" value=\"小明\"></el-option>\n                        <el-option key=\"小红\" label=\"小红\" value=\"小红\"></el-option>\n                        <el-option key=\"小白\" label=\"小白\" value=\"小白\"></el-option>\n                    </el-select>\n                </el-form-item>\n                <el-form-item label=\"日期时间\">\n                    <el-col :span=\"11\">\n                        <el-form-item prop=\"date1\">\n                            <el-date-picker\n                                type=\"date\"\n                                placeholder=\"选择日期\"\n                                v-model=\"form.date1\"\n                                style=\"width: 100%\"\n                            ></el-date-picker>\n                        </el-form-item>\n                    </el-col>\n                    <el-col class=\"line\" :span=\"2\">-</el-col>\n                    <el-col :span=\"11\">\n                        <el-form-item prop=\"date2\">\n                            <el-time-picker placeholder=\"选择时间\" v-model=\"form.date2\" style=\"width: 100%\">\n                            </el-time-picker>\n                        </el-form-item>\n                    </el-col>\n                </el-form-item>\n                <el-form-item label=\"城市级联\" prop=\"options\">\n                    <el-cascader :options=\"options\" v-model=\"form.options\"></el-cascader>\n                </el-form-item>\n                <el-form-item label=\"选择开关\" prop=\"delivery\">\n                    <el-switch v-model=\"form.delivery\"></el-switch>\n                </el-form-item>\n                <el-form-item label=\"多选框\" prop=\"type\">\n                    <el-checkbox-group v-model=\"form.type\">\n                        <el-checkbox label=\"小明\" name=\"type\"></el-checkbox>\n                        <el-checkbox label=\"小红\" name=\"type\"></el-checkbox>\n                        <el-checkbox label=\"小白\" name=\"type\"></el-checkbox>\n                    </el-checkbox-group>\n                </el-form-item>\n                <el-form-item label=\"单选框\" prop=\"resource\">\n                    <el-radio-group v-model=\"form.resource\">\n                        <el-radio label=\"小明\"></el-radio>\n                        <el-radio label=\"小红\"></el-radio>\n                        <el-radio label=\"小白\"></el-radio>\n                    </el-radio-group>\n                </el-form-item>\n                <el-form-item label=\"文本框\" prop=\"desc\">\n                    <el-input type=\"textarea\" rows=\"5\" v-model=\"form.desc\"></el-input>\n                </el-form-item>\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"onSubmit(formRef)\">表单提交</el-button>\n                    <el-button @click=\"onReset(formRef)\">重置表单</el-button>\n                </el-form-item>\n            </el-form>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\" name=\"baseform\">\nimport { reactive, ref } from 'vue';\nimport { ElMessage } from 'element-plus';\nimport type { FormInstance, FormRules } from 'element-plus';\n\nconst options = [\n    {\n        value: 'guangdong',\n        label: '广东省',\n        children: [\n            {\n                value: 'guangzhou',\n                label: '广州市',\n                children: [\n                    {\n                        value: 'tianhe',\n                        label: '天河区',\n                    },\n                    {\n                        value: 'haizhu',\n                        label: '海珠区',\n                    },\n                ],\n            },\n            {\n                value: 'dongguan',\n                label: '东莞市',\n                children: [\n                    {\n                        value: 'changan',\n                        label: '长安镇',\n                    },\n                    {\n                        value: 'humen',\n                        label: '虎门镇',\n                    },\n                ],\n            },\n        ],\n    },\n    {\n        value: 'hunan',\n        label: '湖南省',\n        children: [\n            {\n                value: 'changsha',\n                label: '长沙市',\n                children: [\n                    {\n                        value: 'yuelu',\n                        label: '岳麓区',\n                    },\n                ],\n            },\n        ],\n    },\n];\nconst rules: FormRules = {\n    name: [{ required: true, message: '请输入表单名称', trigger: 'blur' }],\n};\nconst formRef = ref<FormInstance>();\nconst form = reactive({\n    name: '',\n    region: '',\n    date1: '',\n    date2: '',\n    delivery: true,\n    type: ['小明'],\n    resource: '小红',\n    desc: '',\n    options: [],\n});\n// 提交\nconst onSubmit = (formEl: FormInstance | undefined) => {\n    // 表单校验\n    if (!formEl) return;\n    formEl.validate((valid) => {\n        if (valid) {\n            console.log(form);\n            ElMessage.success('提交成功！');\n        } else {\n            return false;\n        }\n    });\n};\n// 重置\nconst onReset = (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.resetFields();\n};\n</script>\n"
  },
  {
    "path": "detection-fontend/src/views/home.vue",
    "content": "<template>\n\t<v-header />\n\t<v-sidebar />\n\t<div class=\"content-box\" :class=\"{ 'content-collapse': sidebar.collapse }\">\n\t\t<v-tags></v-tags>\n\t\t<div class=\"content\">\n\t\t\t<router-view v-slot=\"{ Component }\">\n\t\t\t\t<transition name=\"move\" mode=\"out-in\">\n\t\t\t\t\t<keep-alive :include=\"tags.nameList\">\n\t\t\t\t\t\t<component :is=\"Component\"></component>\n\t\t\t\t\t</keep-alive>\n\t\t\t\t</transition>\n\t\t\t</router-view>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\">\nimport { useSidebarStore } from '../store/sidebar';\nimport { useTagsStore } from '../store/tags';\nimport vHeader from '../components/header.vue';\nimport vSidebar from '../components/sidebar.vue';\nimport vTags from '../components/tags.vue';\n\nconst sidebar = useSidebarStore();\nconst tags = useTagsStore();\n</script>\n"
  },
  {
    "path": "detection-fontend/src/views/icon.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<h2>使用方法</h2>\n\t\t<p style=\"line-height: 50px\">\n\t\t\t直接通过设置类名为 el-icon-lx-iconName 来使用即可。例如：（共{{ iconList.length }}个图标）\n\t\t</p>\n\t\t<p class=\"example-p\">\n\t\t\t<i class=\"el-icon-lx-redpacket_fill\" style=\"font-size: 30px; color: #ff5900\"></i>\n\t\t\t<span>&lt;i class=&quot;el-icon-lx-redpacket_fill&quot;&gt;&lt;/i&gt;</span>\n\t\t</p>\n\t\t<p class=\"example-p\">\n\t\t\t<i class=\"el-icon-lx-weibo\" style=\"font-size: 30px; color: #fd5656\"></i>\n\t\t\t<span>&lt;i class=&quot;el-icon-lx-weibo&quot;&gt;&lt;/i&gt;</span>\n\t\t</p>\n\t\t<p class=\"example-p\">\n\t\t\t<i class=\"el-icon-lx-emojifill\" style=\"font-size: 30px; color: #ffc300\"></i>\n\t\t\t<span>&lt;i class=&quot;el-icon-lx-emojifill&quot;&gt;&lt;/i&gt;</span>\n\t\t</p>\n\t\t<br />\n\t\t<h2>图标</h2>\n\t\t<div class=\"search-box\">\n\t\t\t<el-input class=\"search\" size=\"large\" v-model=\"keyword\" clearable placeholder=\"请输入图标名称\"></el-input>\n\t\t</div>\n\t\t<ul>\n\t\t\t<li class=\"icon-li\" v-for=\"(item, index) in list\" :key=\"index\">\n\t\t\t\t<div class=\"icon-li-content\">\n\t\t\t\t\t<i :class=\"`el-icon-lx-${item}`\"></i>\n\t\t\t\t\t<span>{{ item }}</span>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"icon\">\nimport { computed, ref } from 'vue';\n\nconst iconList: Array<string> = [\n\t'attentionforbid',\n\t'attentionforbidfill',\n\t'attention',\n\t'attentionfill',\n\t'tag',\n\t'tagfill',\n\t'people',\n\t'peoplefill',\n\t'notice',\n\t'noticefill',\n\t'mobile',\n\t'mobilefill',\n\t'voice',\n\t'voicefill',\n\t'unlock',\n\t'lock',\n\t'home',\n\t'homefill',\n\t'delete',\n\t'deletefill',\n\t'notification',\n\t'notificationfill',\n\t'notificationforbidfill',\n\t'like',\n\t'likefill',\n\t'comment',\n\t'commentfill',\n\t'camera',\n\t'camerafill',\n\t'warn',\n\t'warnfill',\n\t'time',\n\t'timefill',\n\t'location',\n\t'locationfill',\n\t'favor',\n\t'favorfill',\n\t'skin',\n\t'skinfill',\n\t'news',\n\t'newsfill',\n\t'record',\n\t'recordfill',\n\t'emoji',\n\t'emojifill',\n\t'message',\n\t'messagefill',\n\t'goods',\n\t'goodsfill',\n\t'crown',\n\t'crownfill',\n\t'move',\n\t'add',\n\t'hot',\n\t'hotfill',\n\t'service',\n\t'servicefill',\n\t'present',\n\t'presentfill',\n\t'pic',\n\t'picfill',\n\t'rank',\n\t'rankfill',\n\t'male',\n\t'female',\n\t'down',\n\t'top',\n\t'recharge',\n\t'rechargefill',\n\t'forward',\n\t'forwardfill',\n\t'info',\n\t'infofill',\n\t'redpacket',\n\t'redpacket_fill',\n\t'roundadd',\n\t'roundaddfill',\n\t'friendadd',\n\t'friendaddfill',\n\t'cart',\n\t'cartfill',\n\t'more',\n\t'moreandroid',\n\t'back',\n\t'right',\n\t'shop',\n\t'shopfill',\n\t'question',\n\t'questionfill',\n\t'roundclose',\n\t'roundclosefill',\n\t'roundcheck',\n\t'roundcheckfill',\n\t'global',\n\t'mail',\n\t'punch',\n\t'exit',\n\t'upload',\n\t'read',\n\t'file',\n\t'link',\n\t'full',\n\t'group',\n\t'friend',\n\t'profile',\n\t'addressbook',\n\t'calendar',\n\t'text',\n\t'copy',\n\t'share',\n\t'wifi',\n\t'vipcard',\n\t'weibo',\n\t'remind',\n\t'refresh',\n\t'filter',\n\t'settings',\n\t'scan',\n\t'qrcode',\n\t'cascades',\n\t'apps',\n\t'sort',\n\t'searchlist',\n\t'search',\n\t'edit'\n];\nconst keyword = ref('');\nconst list = computed(() => {\n\treturn iconList.filter(item => {\n\t\treturn item.indexOf(keyword.value) !== -1;\n\t});\n});\n</script>\n\n<style scoped>\n.example-p {\n\theight: 45px;\n\tdisplay: flex;\n\talign-items: center;\n}\n.search-box {\n\ttext-align: center;\n\tmargin-top: 10px;\n}\n.search {\n\twidth: 300px;\n}\nul,\nli {\n\tlist-style: none;\n}\n.icon-li {\n\tdisplay: inline-block;\n\tpadding: 10px;\n\twidth: 120px;\n\theight: 120px;\n}\n.icon-li-content {\n\tdisplay: flex;\n\theight: 100%;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tcursor: pointer;\n}\n.icon-li-content i {\n\tfont-size: 36px;\n\tcolor: #606266;\n}\n.icon-li-content span {\n\tmargin-top: 10px;\n\tcolor: #787878;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/import.vue",
    "content": "<template>\n    <div>\n        <div class=\"container\">\n            <div class=\"handle-box\">\n                <el-upload\n                    action=\"#\"\n                    :limit=\"1\"\n                    accept=\".xlsx, .xls\"\n                    :show-file-list=\"false\"\n                    :before-upload=\"beforeUpload\"\n                    :http-request=\"handleMany\"\n                >\n                    <el-button class=\"mr10\" type=\"success\">批量导入</el-button>\n                </el-upload>\n                <el-link href=\"/template.xlsx\" target=\"_blank\">下载模板</el-link>\n            </div>\n            <el-table :data=\"tableData\" border class=\"table\" header-cell-class-name=\"table-header\">\n                <el-table-column prop=\"id\" label=\"ID\" width=\"55\" align=\"center\"></el-table-column>\n                <el-table-column prop=\"name\" label=\"姓名\"></el-table-column>\n                <el-table-column prop=\"sno\" label=\"学号\"></el-table-column>\n                <el-table-column prop=\"class\" label=\"班级\"></el-table-column>\n                <el-table-column prop=\"age\" label=\"年龄\"></el-table-column>\n                <el-table-column prop=\"sex\" label=\"性别\"></el-table-column>\n            </el-table>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\" name=\"import\">\nimport { UploadProps } from 'element-plus';\nimport { ref, reactive } from 'vue';\nimport * as XLSX from 'xlsx';\n\ninterface TableItem {\n    id: number;\n    name: string;\n    sno: string;\n    class: string;\n    age: string;\n    sex: string;\n}\n\nconst tableData = ref<TableItem[]>([]);\n// 获取表格数据\nconst getData = () => {\n    tableData.value = [\n        {\n            id: 1,\n            name: '小明',\n            sno: 'S001',\n            class: '一班',\n            age: '10',\n            sex: '男',\n        },\n        {\n            id: 2,\n            name: '小红',\n            sno: 'S002',\n            class: '一班',\n            age: '9',\n            sex: '女',\n        },\n    ];\n};\ngetData();\n\nconst importList = ref<any>([]);\nconst beforeUpload: UploadProps['beforeUpload'] = async (rawFile) => {\n    importList.value = await analysisExcel(rawFile);\n    return true;\n};\nconst analysisExcel = (file: any) => {\n    return new Promise(function (resolve, reject) {\n        const reader = new FileReader();\n        reader.onload = function (e: any) {\n            const data = e.target.result;\n            let datajson = XLSX.read(data, {\n                type: 'binary',\n            });\n\n            const sheetName = datajson.SheetNames[0];\n            const result = XLSX.utils.sheet_to_json(datajson.Sheets[sheetName]);\n            resolve(result);\n        };\n        reader.readAsBinaryString(file);\n    });\n};\n\nconst handleMany = async () => {\n    // 把数据传给服务器后获取最新列表，这里只是示例，不做请求\n    const list = importList.value.map((item: any, index: number) => {\n        return {\n            id: index,\n            name: item['姓名'],\n            sno: item['学号'],\n            class: item['班级'],\n            age: item['年龄'],\n            sex: item['性别'],\n        };\n    });\n    tableData.value.push(...list);\n};\n</script>\n\n<style scoped>\n.handle-box {\n    display: flex;\n    margin-bottom: 20px;\n}\n\n.table {\n    width: 100%;\n    font-size: 14px;\n}\n.mr10 {\n    margin-right: 10px;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/login.vue",
    "content": "<template>\n\t<div class=\"login-wrap\">\n\t\t<div class=\"ms-login\">\n\t\t\t<div class=\"ms-title\">后台管理系统</div>\n\t\t\t<el-form :model=\"param\" :rules=\"rules\" ref=\"login\" label-width=\"0px\" class=\"ms-content\">\n\t\t\t\t<el-form-item prop=\"username\">\n\t\t\t\t\t<el-input v-model=\"param.username\" placeholder=\"username\">\n\t\t\t\t\t\t<template #prepend>\n\t\t\t\t\t\t\t<el-button :icon=\"User\"></el-button>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item prop=\"password\">\n\t\t\t\t\t<el-input\n\t\t\t\t\t\ttype=\"password\"\n\t\t\t\t\t\tplaceholder=\"password\"\n\t\t\t\t\t\tv-model=\"param.password\"\n\t\t\t\t\t\t@keyup.enter=\"submitForm(login)\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<template #prepend>\n\t\t\t\t\t\t\t<el-button :icon=\"Lock\"></el-button>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<div class=\"login-btn\">\n\t\t\t\t\t<el-button type=\"primary\" @click=\"submitForm(login)\">登录</el-button>\n\t\t\t\t</div>\n\t\t\t\t<p class=\"login-tips\">Tips : 用户名和密码随便填。</p>\n\t\t\t</el-form>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, reactive } from 'vue';\nimport { useTagsStore } from '../store/tags';\nimport { usePermissStore } from '../store/permiss';\nimport { useRouter } from 'vue-router';\nimport { ElMessage } from 'element-plus';\nimport type { FormInstance, FormRules } from 'element-plus';\nimport { Lock, User } from '@element-plus/icons-vue';\n\ninterface LoginInfo {\n\tusername: string;\n\tpassword: string;\n}\n\nconst router = useRouter();\nconst param = reactive<LoginInfo>({\n\tusername: 'admin',\n\tpassword: '123123'\n});\n\nconst rules: FormRules = {\n\tusername: [\n\t\t{\n\t\t\trequired: true,\n\t\t\tmessage: '请输入用户名',\n\t\t\ttrigger: 'blur'\n\t\t}\n\t],\n\tpassword: [{ required: true, message: '请输入密码', trigger: 'blur' }]\n};\nconst permiss = usePermissStore();\nconst login = ref<FormInstance>();\nconst submitForm = (formEl: FormInstance | undefined) => {\n\tif (!formEl) return;\n\tformEl.validate((valid: boolean) => {\n\t\tif (valid) {\n\t\t\tElMessage.success('登录成功');\n\t\t\tlocalStorage.setItem('ms_username', param.username);\n\t\t\tconst keys = permiss.defaultList[param.username == 'admin' ? 'admin' : 'user'];\n\t\t\tpermiss.handleSet(keys);\n\t\t\tlocalStorage.setItem('ms_keys', JSON.stringify(keys));\n\t\t\trouter.push('/');\n\t\t} else {\n\t\t\tElMessage.error('登录成功');\n\t\t\treturn false;\n\t\t}\n\t});\n};\n\nconst tags = useTagsStore();\ntags.clearTags();\n</script>\n\n<style scoped>\n.login-wrap {\n\tposition: relative;\n\twidth: 100%;\n\theight: 100%;\n\tbackground-image: url(../assets/img/login-bg.jpg);\n\tbackground-size: 100%;\n}\n.ms-title {\n\twidth: 100%;\n\tline-height: 50px;\n\ttext-align: center;\n\tfont-size: 20px;\n\tcolor: #fff;\n\tborder-bottom: 1px solid #ddd;\n}\n.ms-login {\n\tposition: absolute;\n\tleft: 50%;\n\ttop: 50%;\n\twidth: 350px;\n\tmargin: -190px 0 0 -175px;\n\tborder-radius: 5px;\n\tbackground: rgba(255, 255, 255, 0.3);\n\toverflow: hidden;\n}\n.ms-content {\n\tpadding: 30px 30px;\n}\n.login-btn {\n\ttext-align: center;\n}\n.login-btn button {\n\twidth: 100%;\n\theight: 36px;\n\tmargin-bottom: 10px;\n}\n.login-tips {\n\tfont-size: 12px;\n\tline-height: 30px;\n\tcolor: #fff;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/markdown.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<div class=\"plugins-tips\">\n\t\t\tmd-editor-v3：vue3版本的 markdown 编辑器，配置丰富，请详看文档。 访问地址：\n\t\t\t<a href=\"https://imzbf.github.io/md-editor-v3/index\" target=\"_blank\">md-editor-v3</a>\n\t\t</div>\n\t\t<md-editor class=\"mgb20\" v-model=\"text\" @on-upload-img=\"onUploadImg\" />\n\t\t<el-button type=\"primary\">提交</el-button>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"md\">\nimport { ref } from 'vue';\nimport MdEditor from 'md-editor-v3';\nimport 'md-editor-v3/lib/style.css';\n\nconst text = ref('Hello Editor!');\nconst onUploadImg = (files: any) => {\n\tconsole.log(files);\n};\n</script>\n"
  },
  {
    "path": "detection-fontend/src/views/permission.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<div class=\"plugins-tips\">通过 v-permiss 自定义指令实现权限管理，使用非 admin 账号登录，可查看效果。</div>\n\t\t<div class=\"mgb20\">\n\t\t\t<span class=\"label\">角色：</span>\n\t\t\t<el-select v-model=\"role\" @change=\"handleChange\">\n\t\t\t\t<el-option label=\"超级管理员\" value=\"admin\"></el-option>\n\t\t\t\t<el-option label=\"普通用户\" value=\"user\"></el-option>\n\t\t\t</el-select>\n\t\t</div>\n\t\t<div class=\"mgb20 tree-wrapper\">\n\t\t\t<el-tree\n\t\t\t\tref=\"tree\"\n\t\t\t\t:data=\"data\"\n\t\t\t\tnode-key=\"id\"\n\t\t\t\tdefault-expand-all\n\t\t\t\tshow-checkbox\n\t\t\t\t:default-checked-keys=\"checkedKeys\"\n\t\t\t/>\n\t\t</div>\n\t\t<el-button type=\"primary\" @click=\"onSubmit\">保存权限</el-button>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"permission\">\nimport { ref } from 'vue';\nimport { ElTree } from 'element-plus';\nimport { usePermissStore } from '../store/permiss';\n\nconst role = ref<string>('admin');\n\ninterface Tree {\n\tid: string;\n\tlabel: string;\n\tchildren?: Tree[];\n}\n\nconst data: Tree[] = [\n\t{\n\t\tid: '1',\n\t\tlabel: '系统首页'\n\t},\n\t{\n\t\tid: '2',\n\t\tlabel: '基础表格',\n\t\tchildren: [\n\t\t\t{\n\t\t\t\tid: '15',\n\t\t\t\tlabel: '编辑'\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: '16',\n\t\t\t\tlabel: '删除'\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tid: '3',\n\t\tlabel: 'tab选项卡'\n\t},\n\t{\n\t\tid: '4',\n\t\tlabel: '表单相关',\n\t\tchildren: [\n\t\t\t{\n\t\t\t\tid: '5',\n\t\t\t\tlabel: '基本表单'\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: '6',\n\t\t\t\tlabel: '文件上传'\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: '7',\n\t\t\t\tlabel: '三级菜单',\n\t\t\t\tchildren: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: '8',\n\t\t\t\t\t\tlabel: '富文本编辑器'\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: '9',\n\t\t\t\t\t\tlabel: 'markdown编辑器'\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tid: '10',\n\t\tlabel: '自定义图标'\n\t},\n\t{\n\t\tid: '11',\n\t\tlabel: 'schart图表'\n\t},\n\n\t{\n\t\tid: '13',\n\t\tlabel: '权限管理'\n\t},\n\t{\n\t\tid: '14',\n\t\tlabel: '支持作者'\n\t}\n];\n\nconst permiss = usePermissStore();\n\n// 获取当前权限\nconst checkedKeys = ref<string[]>([]);\nconst getPremission = () => {\n\t// 请求接口返回权限\n\tcheckedKeys.value = permiss.defaultList[role.value];\n};\ngetPremission();\n\n// 保存权限\nconst tree = ref<InstanceType<typeof ElTree>>();\nconst onSubmit = () => {\n\t// 获取选中的权限\n\tconsole.log(tree.value!.getCheckedKeys(false));\n};\n\nconst handleChange = (val: string[]) => {\n\ttree.value!.setCheckedKeys(permiss.defaultList[role.value]);\n};\n</script>\n\n<style scoped>\n.tree-wrapper {\n\tmax-width: 500px;\n}\n.label {\n\tfont-size: 14px;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/table.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"container\">\n\t\t\t<div class=\"handle-box\">\n\t\t\t\t<el-select v-model=\"query.address\" placeholder=\"地址\" class=\"handle-select mr10\">\n\t\t\t\t\t<el-option key=\"1\" label=\"广东省\" value=\"广东省\"></el-option>\n\t\t\t\t\t<el-option key=\"2\" label=\"湖南省\" value=\"湖南省\"></el-option>\n\t\t\t\t</el-select>\n\t\t\t\t<el-input v-model=\"query.name\" placeholder=\"用户名\" class=\"handle-input mr10\"></el-input>\n\t\t\t\t<el-button type=\"primary\" :icon=\"Search\" @click=\"handleSearch\">搜索</el-button>\n\t\t\t\t<el-button type=\"primary\" :icon=\"Plus\">新增</el-button>\n\t\t\t</div>\n\t\t\t<el-table :data=\"tableData\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n\t\t\t\t<el-table-column prop=\"id\" label=\"ID\" width=\"55\" align=\"center\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"name\" label=\"用户名\"></el-table-column>\n\t\t\t\t<el-table-column label=\"账户余额\">\n\t\t\t\t\t<template #default=\"scope\">￥{{ scope.row.money }}</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column label=\"头像(查看大图)\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-image\n\t\t\t\t\t\t\tclass=\"table-td-thumb\"\n\t\t\t\t\t\t\t:src=\"scope.row.thumb\"\n\t\t\t\t\t\t\t:z-index=\"10\"\n\t\t\t\t\t\t\t:preview-src-list=\"[scope.row.thumb]\"\n\t\t\t\t\t\t\tpreview-teleported\n\t\t\t\t\t\t>\n\t\t\t\t\t\t</el-image>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"address\" label=\"地址\"></el-table-column>\n\t\t\t\t<el-table-column label=\"状态\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-tag\n\t\t\t\t\t\t\t:type=\"scope.row.state === '成功' ? 'success' : scope.row.state === '失败' ? 'danger' : ''\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{{ scope.row.state }}\n\t\t\t\t\t\t</el-tag>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\n\t\t\t\t<el-table-column prop=\"date\" label=\"注册时间\"></el-table-column>\n\t\t\t\t<el-table-column label=\"操作\" width=\"220\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-button text :icon=\"Edit\" @click=\"handleEdit(scope.$index, scope.row)\" v-permiss=\"15\">\n\t\t\t\t\t\t\t编辑\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t<el-button text :icon=\"Delete\" class=\"red\" @click=\"handleDelete(scope.$index)\" v-permiss=\"16\">\n\t\t\t\t\t\t\t删除\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t</el-table>\n\t\t\t<div class=\"pagination\">\n\t\t\t\t<el-pagination\n\t\t\t\t\tbackground\n\t\t\t\t\tlayout=\"total, prev, pager, next\"\n\t\t\t\t\t:current-page=\"query.pageIndex\"\n\t\t\t\t\t:page-size=\"query.pageSize\"\n\t\t\t\t\t:total=\"pageTotal\"\n\t\t\t\t\t@current-change=\"handlePageChange\"\n\t\t\t\t></el-pagination>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- 编辑弹出框 -->\n\t\t<el-dialog title=\"编辑\" v-model=\"editVisible\" width=\"30%\">\n\t\t\t<el-form label-width=\"70px\">\n\t\t\t\t<el-form-item label=\"用户名\">\n\t\t\t\t\t<el-input v-model=\"form.name\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"地址\">\n\t\t\t\t\t<el-input v-model=\"form.address\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"editVisible = false\">取 消</el-button>\n\t\t\t\t\t<el-button type=\"primary\" @click=\"saveEdit\">确 定</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"basetable\">\nimport { ref, reactive } from 'vue';\nimport { ElMessage, ElMessageBox } from 'element-plus';\nimport { Delete, Edit, Search, Plus } from '@element-plus/icons-vue';\nimport { fetchData } from '../api/index';\n\ninterface TableItem {\n\tid: number;\n\tname: string;\n\tmoney: string;\n\tstate: string;\n\tdate: string;\n\taddress: string;\n}\n\nconst query = reactive({\n\taddress: '',\n\tname: '',\n\tpageIndex: 1,\n\tpageSize: 10\n});\nconst tableData = ref<TableItem[]>([]);\nconst pageTotal = ref(0);\n// 获取表格数据\nconst getData = () => {\n\tfetchData().then(res => {\n\t\ttableData.value = res.data.list;\n\t\tpageTotal.value = res.data.pageTotal || 50;\n\t});\n};\ngetData();\n\n// 查询操作\nconst handleSearch = () => {\n\tquery.pageIndex = 1;\n\tgetData();\n};\n// 分页导航\nconst handlePageChange = (val: number) => {\n\tquery.pageIndex = val;\n\tgetData();\n};\n\n// 删除操作\nconst handleDelete = (index: number) => {\n\t// 二次确认删除\n\tElMessageBox.confirm('确定要删除吗？', '提示', {\n\t\ttype: 'warning'\n\t})\n\t\t.then(() => {\n\t\t\tElMessage.success('删除成功');\n\t\t\ttableData.value.splice(index, 1);\n\t\t})\n\t\t.catch(() => {});\n};\n\n// 表格编辑时弹窗和保存\nconst editVisible = ref(false);\nlet form = reactive({\n\tname: '',\n\taddress: ''\n});\nlet idx: number = -1;\nconst handleEdit = (index: number, row: any) => {\n\tidx = index;\n\tform.name = row.name;\n\tform.address = row.address;\n\teditVisible.value = true;\n};\nconst saveEdit = () => {\n\teditVisible.value = false;\n\tElMessage.success(`修改第 ${idx + 1} 行成功`);\n\ttableData.value[idx].name = form.name;\n\ttableData.value[idx].address = form.address;\n};\n</script>\n\n<style scoped>\n.handle-box {\n\tmargin-bottom: 20px;\n}\n\n.handle-select {\n\twidth: 120px;\n}\n\n.handle-input {\n\twidth: 300px;\n}\n.table {\n\twidth: 100%;\n\tfont-size: 14px;\n}\n.red {\n\tcolor: #F56C6C;\n}\n.mr10 {\n\tmargin-right: 10px;\n}\n.table-td-thumb {\n\tdisplay: block;\n\tmargin: auto;\n\twidth: 40px;\n\theight: 40px;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/tabs.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<el-tabs v-model=\"message\">\n\t\t\t<el-tab-pane :label=\"`未读消息(${state.unread.length})`\" name=\"first\">\n\t\t\t\t<el-table :data=\"state.unread\" :show-header=\"false\" style=\"width: 100%\">\n\t\t\t\t\t<el-table-column>\n\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t<span class=\"message-title\">{{ scope.row.title }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</el-table-column>\n\t\t\t\t\t<el-table-column prop=\"date\" width=\"180\"></el-table-column>\n\t\t\t\t\t<el-table-column width=\"120\">\n\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t<el-button size=\"small\" @click=\"handleRead(scope.$index)\">标为已读</el-button>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</el-table-column>\n\t\t\t\t</el-table>\n\t\t\t\t<div class=\"handle-row\">\n\t\t\t\t\t<el-button type=\"primary\">全部标为已读</el-button>\n\t\t\t\t</div>\n\t\t\t</el-tab-pane>\n\t\t\t<el-tab-pane :label=\"`已读消息(${state.read.length})`\" name=\"second\">\n\t\t\t\t<template v-if=\"message === 'second'\">\n\t\t\t\t\t<el-table :data=\"state.read\" :show-header=\"false\" style=\"width: 100%\">\n\t\t\t\t\t\t<el-table-column>\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<span class=\"message-title\">{{ scope.row.title }}</span>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t\t<el-table-column prop=\"date\" width=\"150\"></el-table-column>\n\t\t\t\t\t\t<el-table-column width=\"120\">\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<el-button type=\"danger\" @click=\"handleDel(scope.$index)\">删除</el-button>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t</el-table>\n\t\t\t\t\t<div class=\"handle-row\">\n\t\t\t\t\t\t<el-button type=\"danger\">删除全部</el-button>\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</el-tab-pane>\n\t\t\t<el-tab-pane :label=\"`回收站(${state.recycle.length})`\" name=\"third\">\n\t\t\t\t<template v-if=\"message === 'third'\">\n\t\t\t\t\t<el-table :data=\"state.recycle\" :show-header=\"false\" style=\"width: 100%\">\n\t\t\t\t\t\t<el-table-column>\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<span class=\"message-title\">{{ scope.row.title }}</span>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t\t<el-table-column prop=\"date\" width=\"150\"></el-table-column>\n\t\t\t\t\t\t<el-table-column width=\"120\">\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<el-button @click=\"handleRestore(scope.$index)\">还原</el-button>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t</el-table>\n\t\t\t\t\t<div class=\"handle-row\">\n\t\t\t\t\t\t<el-button type=\"danger\">清空回收站</el-button>\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</el-tab-pane>\n\t\t</el-tabs>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"tabs\">\nimport { ref, reactive } from 'vue';\n\nconst message = ref('first');\nconst state = reactive({\n\tunread: [\n\t\t{\n\t\t\tdate: '2018-04-19 20:00:00',\n\t\t\ttitle: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'\n\t\t},\n\t\t{\n\t\t\tdate: '2018-04-19 21:00:00',\n\t\t\ttitle: '今晚12点整发大红包，先到先得'\n\t\t}\n\t],\n\tread: [\n\t\t{\n\t\t\tdate: '2018-04-19 20:00:00',\n\t\t\ttitle: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'\n\t\t}\n\t],\n\trecycle: [\n\t\t{\n\t\t\tdate: '2018-04-19 20:00:00',\n\t\t\ttitle: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'\n\t\t}\n\t]\n});\n\nconst handleRead = (index: number) => {\n\tconst item = state.unread.splice(index, 1);\n\tstate.read = item.concat(state.read);\n};\nconst handleDel = (index: number) => {\n\tconst item = state.read.splice(index, 1);\n\tstate.recycle = item.concat(state.recycle);\n};\nconst handleRestore = (index: number) => {\n\tconst item = state.recycle.splice(index, 1);\n\tstate.read = item.concat(state.read);\n};\n</script>\n\n<style>\n.message-title {\n\tcursor: pointer;\n}\n.handle-row {\n\tmargin-top: 30px;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/upload.vue",
    "content": "<template>\n    <div class=\"container\">\n        <div class=\"content-title\">支持拖拽</div>\n        <div class=\"plugins-tips\">\n            Element Plus自带上传组件。 访问地址：\n            <a href=\"https://element-plus.org/zh-CN/component/upload.html\" target=\"_blank\">Element Plus Upload</a>\n        </div>\n        <el-upload\n            class=\"upload-demo\"\n            drag\n            action=\"http://jsonplaceholder.typicode.com/api/posts/\"\n            multiple\n            :on-change=\"handle\"\n        >\n            <el-icon class=\"el-icon--upload\"><upload-filled /></el-icon>\n            <div class=\"el-upload__text\">\n                将文件拖到此处，或\n                <em>点击上传</em>\n            </div>\n        </el-upload>\n\n        <div class=\"content-title\">支持裁剪</div>\n        <div class=\"plugins-tips\">\n            vue-cropperjs：一个封装了 cropperjs 的 Vue 组件。 访问地址：\n            <a href=\"https://github.com/Agontuk/vue-cropperjs\" target=\"_blank\">vue-cropperjs</a>。 示例请查看\n            <router-link to=\"/user\">个人中心</router-link>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nconst handle = (rawFile: any) => {\n    console.log(rawFile);\n};\n</script>\n\n<style scoped>\n.content-title {\n    font-weight: 400;\n    line-height: 50px;\n    margin: 10px 0;\n    font-size: 22px;\n    color: #1f2f3d;\n}\n.upload-demo {\n    width: 360px;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/views/user.vue",
    "content": "<template>\n\t<div>\n\t\t<el-row :gutter=\"20\">\n\t\t\t<el-col :span=\"12\">\n\t\t\t\t<el-card shadow=\"hover\">\n\t\t\t\t\t<template #header>\n\t\t\t\t\t\t<div class=\"clearfix\">\n\t\t\t\t\t\t\t<span>基础信息</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\t\t\t\t\t<div class=\"info\">\n\t\t\t\t\t\t<div class=\"info-image\" @click=\"showDialog\">\n\t\t\t\t\t\t\t<el-avatar :size=\"100\" :src=\"avatarImg\" />\n\t\t\t\t\t\t\t<span class=\"info-edit\">\n\t\t\t\t\t\t\t\t<i class=\"el-icon-lx-camerafill\"></i>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"info-name\">{{ name }}</div>\n\t\t\t\t\t\t<div class=\"info-desc\">SCAU_DS</div>\n\t\t\t\t\t</div>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t\t<el-col :span=\"12\">\n\t\t\t\t<el-card shadow=\"hover\">\n\t\t\t\t\t<template #header>\n\t\t\t\t\t\t<div class=\"clearfix\">\n\t\t\t\t\t\t\t<span>账户编辑</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\t\t\t\t\t<el-form label-width=\"90px\">\n\t\t\t\t\t\t<el-form-item label=\"用户名：\"> {{ name }} </el-form-item>\n\t\t\t\t\t\t<el-form-item label=\"旧密码：\">\n\t\t\t\t\t\t\t<el-input type=\"password\" v-model=\"form.old\"></el-input>\n\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t\t<el-form-item label=\"新密码：\">\n\t\t\t\t\t\t\t<el-input type=\"password\" v-model=\"form.new\"></el-input>\n\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t\t<el-form-item label=\"个人简介：\">\n\t\t\t\t\t\t\t<el-input v-model=\"form.desc\"></el-input>\n\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t\t<el-form-item>\n\t\t\t\t\t\t\t<el-button type=\"primary\" @click=\"onSubmit\">保存</el-button>\n\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t</el-form>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t</el-row>\n\t\t<el-dialog title=\"裁剪图片\" v-model=\"dialogVisible\" width=\"600px\">\n\t\t\t<vue-cropper\n\t\t\t\tref=\"cropper\"\n\t\t\t\t:src=\"imgSrc\"\n\t\t\t\t:ready=\"cropImage\"\n\t\t\t\t:zoom=\"cropImage\"\n\t\t\t\t:cropmove=\"cropImage\"\n\t\t\t\tstyle=\"width: 100%; height: 400px\"\n\t\t\t></vue-cropper>\n\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button class=\"crop-demo-btn\" type=\"primary\"\n\t\t\t\t\t\t>选择图片\n\t\t\t\t\t\t<input class=\"crop-input\" type=\"file\" name=\"image\" accept=\"image/*\" @change=\"setImage\" />\n\t\t\t\t\t</el-button>\n\t\t\t\t\t<el-button type=\"primary\" @click=\"saveAvatar\">上传并保存</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"user\">\nimport { reactive, ref } from 'vue';\nimport VueCropper from 'vue-cropperjs';\nimport 'cropperjs/dist/cropper.css';\n// import avatar from '../assets/img/img.jpg';\nimport avatar from '../assets/img/img.png';\n\nconst name = localStorage.getItem('ms_username');\nconst form = reactive({\n\told: '',\n\tnew: '',\n\t// desc: '不可能！我的代码怎么可能会有bug！'\n  desc: '',\n});\nconst onSubmit = () => {};\n\nconst avatarImg = ref(avatar);\nconst imgSrc = ref('');\nconst cropImg = ref('');\nconst dialogVisible = ref(false);\nconst cropper: any = ref();\n\nconst showDialog = () => {\n\tdialogVisible.value = true;\n\timgSrc.value = avatarImg.value;\n};\n\nconst setImage = (e: any) => {\n\tconst file = e.target.files[0];\n\tif (!file.type.includes('image/')) {\n\t\treturn;\n\t}\n\tconst reader = new FileReader();\n\treader.onload = (event: any) => {\n\t\tdialogVisible.value = true;\n\t\timgSrc.value = event.target.result;\n\t\tcropper.value && cropper.value.replace(event.target.result);\n\t};\n\treader.readAsDataURL(file);\n};\n\nconst cropImage = () => {\n\tcropImg.value = cropper.value.getCroppedCanvas().toDataURL();\n};\n\nconst saveAvatar = () => {\n\tavatarImg.value = cropImg.value;\n\tdialogVisible.value = false;\n};\n</script>\n\n<style scoped>\n.info {\n\ttext-align: center;\n\tpadding: 35px 0;\n}\n.info-image {\n\tposition: relative;\n\tmargin: auto;\n\twidth: 100px;\n\theight: 100px;\n\tbackground: #f8f8f8;\n\tborder: 1px solid #eee;\n\tborder-radius: 50px;\n\toverflow: hidden;\n}\n\n.info-edit {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\tposition: absolute;\n\tleft: 0;\n\ttop: 0;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: rgba(0, 0, 0, 0.5);\n\topacity: 0;\n\ttransition: opacity 0.3s ease;\n}\n.info-edit i {\n\tcolor: #eee;\n\tfont-size: 25px;\n}\n.info-image:hover .info-edit {\n\topacity: 1;\n}\n.info-name {\n\tmargin: 15px 0 10px;\n\tfont-size: 24px;\n\tfont-weight: 500;\n\tcolor: #262626;\n}\n.crop-demo-btn {\n\tposition: relative;\n}\n.crop-input {\n\tposition: absolute;\n\twidth: 100px;\n\theight: 40px;\n\tleft: 0;\n\ttop: 0;\n\topacity: 0;\n\tcursor: pointer;\n}\n</style>\n"
  },
  {
    "path": "detection-fontend/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n\ndeclare module '*.vue' {\n  import type { DefineComponent } from 'vue'\n  const component: DefineComponent<{}, {}, any>\n  export default component\n}\n\ndeclare module 'vue-schart';\ndeclare module 'vue-cropperjs';"
  },
  {
    "path": "detection-fontend/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"sourceMap\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"skipLibCheck\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\",\"src/**/*.vue\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "detection-fontend/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "detection-fontend/vite.config.ts",
    "content": "import { defineConfig } from 'vite';\nimport vue from '@vitejs/plugin-vue';\nimport VueSetupExtend from 'vite-plugin-vue-setup-extend';\nimport AutoImport from 'unplugin-auto-import/vite';\nimport Components from 'unplugin-vue-components/vite';\nimport { ElementPlusResolver } from 'unplugin-vue-components/resolvers';\nexport default defineConfig({\n\tbase: './',\n\tplugins: [\n\t\tvue(),\n\t\tVueSetupExtend(),\n\t\tAutoImport({\n\t\t\tresolvers: [ElementPlusResolver()]\n\t\t}),\n\t\tComponents({\n\t\t\tresolvers: [ElementPlusResolver()]\n\t\t})\n\t],\n\toptimizeDeps: {\n\t\tinclude: ['schart.js']\n\t}\n});\n"
  }
]