diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..15b8fbc Binary files /dev/null and b/.DS_Store differ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..38e0acd --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 孤傲的小笼包 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/安装资源/数据库脚本.sql b/docs/安装资源/数据库脚本.sql new file mode 100644 index 0000000..0ddb39b --- /dev/null +++ b/docs/安装资源/数据库脚本.sql @@ -0,0 +1,847 @@ +/* + Navicat Premium Data Transfer + + Source Server : 127.0.0.1-本地库 + Source Server Type : MySQL + Source Server Version : 80028 (8.0.28) + Source Host : localhost:3306 + Source Schema : yf_exam_lite + + Target Server Type : MySQL + Target Server Version : 80028 (8.0.28) + File Encoding : 65001 + + Date: 20/02/2023 10:38:52 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for el_exam +-- ---------------------------- +DROP TABLE IF EXISTS `el_exam`; +CREATE TABLE `el_exam` ( + `id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID', + `title` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '考试名称', + `content` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '考试描述', + `open_type` int NOT NULL DEFAULT '1' COMMENT '1公开2部门3定员', + `state` int NOT NULL DEFAULT '0' COMMENT '考试状态', + `time_limit` tinyint NOT NULL DEFAULT '0' COMMENT '是否限时', + `start_time` datetime DEFAULT NULL COMMENT '开始时间', + `end_time` datetime DEFAULT NULL COMMENT '结束时间', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `total_score` int NOT NULL DEFAULT '0' COMMENT '总分数', + `total_time` int NOT NULL DEFAULT '0' COMMENT '总时长(分钟)', + `qualify_score` int NOT NULL DEFAULT '0' COMMENT '及格分数', + PRIMARY KEY (`id`) USING BTREE, + KEY `open_type` (`open_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试主表'; + +-- ---------------------------- +-- Records of el_exam +-- ---------------------------- +BEGIN; +INSERT INTO `el_exam` (`id`, `title`, `content`, `open_type`, `state`, `time_limit`, `start_time`, `end_time`, `create_time`, `update_time`, `total_score`, `total_time`, `qualify_score`) VALUES ('1587621704140427265', '【云帆演示】考试', '【云帆演示】考试', 1, 0, 0, NULL, NULL, '2022-11-02 09:44:46', '2022-11-02 09:44:46', 100, 30, 60); +COMMIT; + +-- ---------------------------- +-- Table structure for el_exam_depart +-- ---------------------------- +DROP TABLE IF EXISTS `el_exam_depart`; +CREATE TABLE `el_exam_depart` ( + `id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'ID', + `exam_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '考试ID', + `depart_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '部门ID', + PRIMARY KEY (`id`) USING BTREE, + KEY `exam_id` (`exam_id`), + KEY `depart_id` (`depart_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试部门'; + +-- ---------------------------- +-- Records of el_exam_depart +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for el_exam_repo +-- ---------------------------- +DROP TABLE IF EXISTS `el_exam_repo`; +CREATE TABLE `el_exam_repo` ( + `id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID', + `exam_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '考试ID', + `repo_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题库ID', + `radio_count` int NOT NULL DEFAULT '0' COMMENT '单选题数量', + `radio_score` int NOT NULL DEFAULT '0' COMMENT '单选题分数', + `multi_count` int NOT NULL DEFAULT '0' COMMENT '多选题数量', + `multi_score` int NOT NULL DEFAULT '0' COMMENT '多选题分数', + `judge_count` int NOT NULL DEFAULT '0' COMMENT '判断题数量', + `judge_score` int NOT NULL DEFAULT '0' COMMENT '判断题分数', + `saq_count` int NOT NULL DEFAULT '0' COMMENT '简答题数量', + `saq_score` int NOT NULL DEFAULT '0' COMMENT '简答题分数', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `exam_repo_id` (`exam_id`,`repo_id`), + KEY `rule_id` (`exam_id`) USING BTREE, + KEY `repo_id` (`repo_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试题库'; + +-- ---------------------------- +-- Records of el_exam_repo +-- ---------------------------- +BEGIN; +INSERT INTO `el_exam_repo` (`id`, `exam_id`, `repo_id`, `radio_count`, `radio_score`, `multi_count`, `multi_score`, `judge_count`, `judge_score`, `saq_count`, `saq_score`) VALUES ('1627496519370297345', '1587621704140427265', '1587622451624120321', 2, 10, 6, 10, 2, 10, 0, 0); +COMMIT; + +-- ---------------------------- +-- Table structure for el_paper +-- ---------------------------- +DROP TABLE IF EXISTS `el_paper`; +CREATE TABLE `el_paper` ( + `id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '试卷ID', + `user_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID', + `depart_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '部门ID', + `exam_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '规则ID', + `title` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '考试标题', + `total_time` int NOT NULL DEFAULT '0' COMMENT '考试时长', + `user_time` int NOT NULL DEFAULT '0' COMMENT '用户时长', + `total_score` int NOT NULL DEFAULT '0' COMMENT '试卷总分', + `qualify_score` int NOT NULL DEFAULT '0' COMMENT '及格分', + `obj_score` int NOT NULL DEFAULT '0' COMMENT '客观分', + `subj_score` int NOT NULL DEFAULT '0' COMMENT '主观分', + `user_score` int NOT NULL COMMENT '用户得分', + `has_saq` tinyint NOT NULL DEFAULT '0' COMMENT '是否包含简答题', + `state` int NOT NULL DEFAULT '1' COMMENT '试卷状态', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `limit_time` datetime DEFAULT NULL COMMENT '截止时间', + PRIMARY KEY (`id`) USING BTREE, + KEY `user_id` (`user_id`) USING BTREE, + KEY `exam_id` (`exam_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试记录'; + +-- ---------------------------- +-- Records of el_paper +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for el_paper_qu +-- ---------------------------- +DROP TABLE IF EXISTS `el_paper_qu`; +CREATE TABLE `el_paper_qu` ( + `id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID', + `paper_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '试卷ID', + `qu_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目ID', + `qu_type` int NOT NULL COMMENT '题目类型', + `answered` tinyint NOT NULL DEFAULT '0' COMMENT '是否已答', + `answer` varchar(5000) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '主观答案', + `sort` int NOT NULL DEFAULT '0' COMMENT '问题排序', + `score` int NOT NULL DEFAULT '0' COMMENT '单题分分值', + `actual_score` int NOT NULL DEFAULT '0' COMMENT '实际得分(主观题)', + `is_right` tinyint NOT NULL DEFAULT '0' COMMENT '是否答对', + PRIMARY KEY (`id`) USING BTREE, + KEY `paper_id` (`paper_id`) USING BTREE, + KEY `qu_id` (`qu_id`) USING BTREE, + KEY `paper_qu_id` (`paper_id`,`qu_id`) USING BTREE, + KEY `sort` (`sort`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试记录考题'; + +-- ---------------------------- +-- Records of el_paper_qu +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for el_paper_qu_answer +-- ---------------------------- +DROP TABLE IF EXISTS `el_paper_qu_answer`; +CREATE TABLE `el_paper_qu_answer` ( + `id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '自增ID', + `paper_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '试卷ID', + `answer_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '回答项ID', + `qu_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目ID', + `is_right` tinyint NOT NULL DEFAULT '0' COMMENT '是否正确项', + `checked` tinyint NOT NULL DEFAULT '0' COMMENT '是否选中', + `sort` int NOT NULL DEFAULT '0' COMMENT '排序', + `abc` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '选项标签', + PRIMARY KEY (`id`) USING BTREE, + KEY `paper_id` (`paper_id`) USING BTREE, + KEY `qu_id` (`qu_id`) USING BTREE, + KEY `paper_qu_id` (`paper_id`,`qu_id`) USING BTREE, + KEY `sort` (`sort`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试记录答案'; + +-- ---------------------------- +-- Records of el_paper_qu_answer +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for el_qu +-- ---------------------------- +DROP TABLE IF EXISTS `el_qu`; +CREATE TABLE `el_qu` ( + `id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目ID', + `qu_type` int NOT NULL COMMENT '题目类型', + `level` int NOT NULL DEFAULT '1' COMMENT '1普通,2较难', + `image` varchar(500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '题目图片', + `content` varchar(2000) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目内容', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', + `remark` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '题目备注', + `analysis` varchar(2000) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '整题解析', + PRIMARY KEY (`id`) USING BTREE, + KEY `qu_type` (`qu_type`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='试题主表'; + +-- ---------------------------- +-- Records of el_qu +-- ---------------------------- +BEGIN; +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642263625729', 1, 1, '', '五个答案中哪个是最好的类比?女儿对于父亲相当于侄女对于', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642393649153', 1, 1, '', '五个答案中哪个是最好的类比?皮对于树相当于鳞对于', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642473340930', 1, 1, '', '火车守车(车尾)长6.4米。机车的长度等于守车的长加上半节车厢的长。车厢长度等于守车长加上机车长。火车的机车、车厢、守车共长多少米?', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642557227009', 1, 1, '', '角对于元相当于小时对于', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642641113089', 1, 1, '', '如果把这个大立方体的六个面全部涂上黑色,然后按图中虚线把它切成36个小方块,两面有黑色的小方块有多少个?', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642699833345', 1, 1, '', '找出与众不同的一个:', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642762747906', 2, 1, '', '以下哪些是中国的特别行政区?', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642838245378', 2, 1, '', '中国东北三省是指()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642909548545', 3, 1, '', '咖啡的故乡是非洲吗?', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642976657410', 3, 1, '', '世界上最长的山脉安第斯山脉', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643039571970', 1, 1, '', '西亚波斯湾沿岸比较富裕,其原因是()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '西亚波斯湾沿岸国家比较富裕,是因为大量出口石油资源.'); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643102486530', 1, 1, '', '我国最北面的城市是哪个()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '漠河市,隶属黑龙江省大兴安岭地区。 [1] 地处黑龙江省北部。西与内蒙古自治区呼伦贝尔市额尔古纳市为邻,南与内蒙古自治区根河市和呼中区交界,东与塔河县接壤,北隔黑龙江与俄罗斯外贝加尔边疆区(原赤塔州)和阿穆尔州相望,是中国最北端的县级行政区;地势南高北低,南北呈坡降趋势,属于寒温带大陆性季风气候。下辖6个镇,总面积18427平方千米。 [2] 根据第七次人口普查数据,截至2020年11月1日零时,漠河市常住人口为54036人。'); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643173789698', 1, 1, '', '人们把社会生产的各个部门划分为三类产业,下列属于第三产业的是()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '通常人们把生产的各部门划分为三类产业.农业是第一产业,包括种植业、林业、畜牧业、渔业等;工业和建筑业是第二产业;流通部门和服务部门是第三产业'); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643240898561', 1, 1, '', '亚洲人中分布不是很均匀,其中人中较稀疏的部分是()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '人口稠密地区都处在中低纬度、近海、平原地区,亚洲人口稀疏的地区在纬度位置较高的北亚,沙漠广布的西亚及气候干旱的中亚地区.'); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643312201730', 1, 1, '', '亚洲各国经济发展不平衡,下列国家中人均国民生产总值最高的是()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '本题还可以考查人口:人口超亿的国家有中国、印度、印度尼西亚、巴基斯坦、日本、孟加拉国等6个;各大洲人口排序:亚非欧南北美大洋洲;各大洲人口增长率:非南美亚大洋北美欧;著名的民族文化:亚洲三个人类文明发祥地:华夏文化(黄河——长江中下游地区)、印度河流域文化、两河流域文化(由幼法拉底河和底格里斯河冲积而成的美索不达米亚平原地区);还有恒河文化、阿拉伯文化等。亚洲只有日本属于发达国家。'); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643396087810', 1, 1, '', '地球上0度经线和0度纬线的长度相比()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '0度经线和0度纬线相比()0度纬线的长度是0度经线的2倍。 因为经线是半圆,赤道是大圆。'); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643467390977', 1, 1, '', '下列国家人口超过1亿的南亚国家是()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '世界人口超过一亿的国家,从多到少依次为:中国、印度、美国、印度尼西亚、巴西、巴基斯坦、俄罗斯、孟加拉国、尼日利亚、日本和墨西哥共有11个.其中属于南亚的是印度、巴基斯坦和孟加拉国.'); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643551277058', 1, 1, '', '世界上跨东西、南北距离最长的大洲是()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '在全球的七大洲中,亚洲是世界上面积最大的一洲,也是南北跨纬度最多,热量差异大; 东西跨经度仅次于南极洲,东西距离最长的大洲;大洋洲是世界上最小的一洲 考点:本题主要考查大洲和大洋。'); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643626774530', 1, 1, '', '地球公转会产生()', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '地球自转产生的现象有: 1、昼夜更替现象,向着太阳的半球,是白天,背着太阳的半球,是黑夜; 2、南北半球的地转偏向力引起的各种运动旋转现象; 3、地球上不同经度的地方,有不同的地方时;经度每隔15度,地方时相差一小时; 4、东西部地区的时间差现象,生物作息规律现象; 5、对地球形状的影响.地球自转所产生的惯性离心力,使得地球由两级向赤道逐渐膨胀,成为目前略扁的旋转椭球体。 地球公转产生的现象有: 1、根据太阳高度的差异,划分出五带:北寒、北温、热带、南温、南寒; 2、根据获得热量多少的时间差异,划分出四季:春、夏、秋、冬; 3、昼夜长短的变化现象; 4、天象位置的变化;生物生长规律现象. 5、正午太阳高度的变化;夏至日太阳直射北回归线,全球正午太阳高度从北回归线向南北两侧逐渐递减;二分日太阳直射赤道,全球正午太阳高度从赤道向两极递减,全球昼夜平分;冬至日太阳直射南回归线,全球正午高度从南回归线向南北两侧逐渐递减,南回归线及其以南的地区正午太阳高度达到最大值,北半球各纬度正午太阳高度达到最小值。'); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643693883394', 2, 1, '', '南极旅游的兴起,请下列哪些因素有关()', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643760992258', 2, 1, '', '交通运输线路(公路、铁路),选址的原因有()', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643823906818', 2, 1, 'http://localhost:8201/upload/file/2022/11/07/1589528183307354113.jpg', '地球自转产生的现象有()', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643895209985', 2, 1, '', '地球公转产生的现象有()', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643979096066', 3, 1, '', '地球上出现的潮汐是由于地月吸引力。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622644054593538', 3, 1, '', '被称为“万园之园”的我国古典园林是颐和园。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622644134285314', 3, 1, '', '世界最长的山脉是安第斯山脉。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', ''); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622644201394178', 3, 1, '', '仪表显示当前发动机转速是6000转克/分钟。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '仪表显示当前车速是20公里/小时。'); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622644281085953', 3, 1, '', '仪表显示当前冷却液的温度是90°C。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '水温表:指示发动机冷却液的温度,单位为摄氏度(C)。水温表指针所指的位置显示当前冷却液的温度。\n如图所示,指针指的位置是90,表示当前冷却液的温度是9o°C。'); +INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622644356583425', 1, 1, '', '我国面积最大的湖泊是()。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '青海湖是我国最大的湖泊,它是一个咸水湖,面积约4450平方公里,说起来也不小了,但是如果把它放到全世界范围来看的话,青海湖实际上并非大型湖泊,单就面积而言,在全世界排名第34位,和我国国土面积世界第三的位置很不相称,世界最大湖泊里海的面积(38万平方公里),就相当于84个青海湖。'); +COMMIT; + +-- ---------------------------- +-- Table structure for el_qu_answer +-- ---------------------------- +DROP TABLE IF EXISTS `el_qu_answer`; +CREATE TABLE `el_qu_answer` ( + `id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '答案ID', + `qu_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '问题ID', + `is_right` tinyint NOT NULL DEFAULT '0' COMMENT '是否正确', + `image` varchar(500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '选项图片', + `content` varchar(5000) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '答案内容', + `analysis` varchar(5000) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '答案分析', + PRIMARY KEY (`id`) USING BTREE, + KEY `qu_id` (`qu_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='试题答案选项'; + +-- ---------------------------- +-- Records of el_qu_answer +-- ---------------------------- +BEGIN; +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642309763073', '1587622642263625729', 0, '', '母亲', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642318151681', '1587622642263625729', 0, '', '哥哥', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642318151682', '1587622642263625729', 0, '', '侄子', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642318151683', '1587622642263625729', 0, '', '表兄', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642318151684', '1587622642263625729', 1, '', '叔叔', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642402037761', '1587622642393649153', 0, '', '鳃', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642402037762', '1587622642393649153', 0, '', '大海', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642402037763', '1587622642393649153', 0, '', '渔夫', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642402037764', '1587622642393649153', 1, '', '鱼', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642402037765', '1587622642393649153', 0, '', '鳍', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642490118145', '1587622642473340930', 0, '', '25.6米', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642490118146', '1587622642473340930', 0, '', '36米', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642490118147', '1587622642473340930', 1, '', '51.2米', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642490118148', '1587622642473340930', 0, '', '64.4米', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642490118149', '1587622642473340930', 0, '', '76.2米', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642569809922', '1587622642557227009', 1, '', '分', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642569809923', '1587622642557227009', 0, '', '秒', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642569809924', '1587622642557227009', 0, '', '月', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642569809925', '1587622642557227009', 0, '', '日', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642569809926', '1587622642557227009', 0, '', '钟', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642649501697', '1587622642641113089', 0, '', '10', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642649501698', '1587622642641113089', 0, '', '12', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642649501699', '1587622642641113089', 1, '', '16', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642649501700', '1587622642641113089', 0, '', '20', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642649501701', '1587622642641113089', 0, '', '8', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642708221953', '1587622642699833345', 0, '', '西安', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642708221954', '1587622642699833345', 0, '', '郑州', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642708221955', '1587622642699833345', 1, '', '哈尔滨', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642708221956', '1587622642699833345', 0, '', '昆明', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642708221957', '1587622642699833345', 0, '', '南昌', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642771136513', '1587622642762747906', 1, '', '香港', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642775330818', '1587622642762747906', 1, '', '澳门', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642775330819', '1587622642762747906', 0, '', '珠海', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642775330820', '1587622642762747906', 0, '', '重庆', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642846633985', '1587622642838245378', 1, '', '黑龙江省', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642846633986', '1587622642838245378', 1, '', '吉林省', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642846633987', '1587622642838245378', 1, '', '辽宁省', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642846633988', '1587622642838245378', 0, '', '河北省', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642917937153', '1587622642909548545', 1, '', '正确', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642917937154', '1587622642909548545', 0, '', '错误', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642985046018', '1587622642976657410', 1, '', '正确', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642989240321', '1587622642976657410', 0, '', '错误', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643047960578', '1587622643039571970', 0, '', '工亚基础雄厚', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643047960579', '1587622643039571970', 1, '', '拥有丰富的石油资源', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643047960580', '1587622643039571970', 0, '', '第三产业在国内生产总值中的比重大', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643052154882', '1587622643039571970', 0, '', '大力发展出口加工工业', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643110875137', '1587622643102486530', 0, '', '铁岭', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643110875138', '1587622643102486530', 0, '', '齐齐哈尔', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643110875139', '1587622643102486530', 0, '', '沈阳', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643110875140', '1587622643102486530', 1, '', '漠河', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643182178305', '1587622643173789698', 0, '', '工业', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643182178306', '1587622643173789698', 0, '', '建筑业', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643182178307', '1587622643173789698', 1, '', '服务业', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643182178308', '1587622643173789698', 0, '', '农业', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643249287170', '1587622643240898561', 0, '', '东亚', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643253481474', '1587622643240898561', 0, '', '东南亚', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643253481475', '1587622643240898561', 0, '', '南亚', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643253481476', '1587622643240898561', 1, '', '西亚', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643324784642', '1587622643312201730', 0, '', '韩国', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643324784643', '1587622643312201730', 0, '', '新加坡', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643324784644', '1587622643312201730', 1, '', '日本', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643324784645', '1587622643312201730', 0, '', '马来西亚', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643404476417', '1587622643396087810', 1, '', '0度纬线稍长', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643404476418', '1587622643396087810', 0, '', '0度经线稍长', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643404476419', '1587622643396087810', 0, '', '相等', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643404476420', '1587622643396087810', 0, '', '0度经线不到0度纬线的1/2', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643479973889', '1587622643467390977', 1, '', '孟加拉国', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643479973890', '1587622643467390977', 0, '', '印度尼西亚', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643479973891', '1587622643467390977', 0, '', '日本', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643479973892', '1587622643467390977', 0, '', '韩国', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643563859970', '1587622643551277058', 0, '', '非洲', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643563859971', '1587622643551277058', 0, '', '北美洲', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643563859972', '1587622643551277058', 0, '', '大洋洲', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643563859973', '1587622643551277058', 1, '', '亚洲', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643635163138', '1587622643626774530', 1, '', '四季现象', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643639357441', '1587622643626774530', 0, '', '昼夜交替', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643639357442', '1587622643626774530', 0, '', '时间差异', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643639357443', '1587622643626774530', 0, '', '日期差异', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643702272001', '1587622643693883394', 1, '', '人们的求知、探秘和猎奇欲望的增长', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643702272002', '1587622643693883394', 1, '', '交通工具的发展', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643702272003', '1587622643693883394', 0, '', '促使不同国家的地区的文化交增进友谊', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643702272004', '1587622643693883394', 1, '', '科学技术的进步', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643765186561', '1587622643760992258', 1, '', '等高线稀疏', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643769380865', '1587622643760992258', 1, '', '地形坡度和缓', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643769380866', '1587622643760992258', 0, '', '隧道多', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643769380867', '1587622643760992258', 0, '', '地形复杂', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643832295426', '1587622643823906818', 1, 'http://localhost:8201/upload/file/2022/11/07/1589528211451133954.jpg', '南北半球的地转偏向力引起的各种运动旋转现象;', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643832295427', '1587622643823906818', 1, '', '地球上不同经度的地方,有不同的地方时;经度每隔15度,地方时相差一小时;', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643832295428', '1587622643823906818', 1, 'http://localhost:8201/upload/file/2022/11/07/1589528315486650369.jpeg', '东西部地区的时间差现象,生物作息规律现象;', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643832295429', '1587622643823906818', 1, '', '对地球形状的影响.地球自转所产生的惯性离心力,使得地球由两级向赤道逐渐膨胀,成为目前略扁的旋转椭球体。', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643832295430', '1587622643823906818', 1, '', '昼夜更替现象,向着太阳的半球,是白天,背着太阳的半球,是黑夜;', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643903598594', '1587622643895209985', 1, '', '根据太阳高度的差异,划分出五带:北寒、北温、热带、南温、南寒;', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643903598595', '1587622643895209985', 1, '', '根据获得热量多少的时间差异,划分出四季:春、夏、秋、冬;', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643903598596', '1587622643895209985', 1, '', '昼夜长短的变化现象;', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643903598597', '1587622643895209985', 1, '', '正午太阳高度的变化;夏至日太阳直射北回归线,全球正午太阳高度从北回归线向南北两侧逐渐递减;二分日太阳直射赤道,全球正午太阳高度从赤道向两极递减,全球昼夜平分;冬至日太阳直射南回归线,全球正午高度从南回归线向南北两侧逐渐递减,南回归线及其以南的地区正午太阳高度达到最大值,北半球各纬度正午太阳高度达到最小值。', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643983290369', '1587622643979096066', 1, '', '正确', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643987484674', '1587622643979096066', 0, '', '错误', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644058787842', '1587622644054593538', 0, '', '正确', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644062982146', '1587622644054593538', 1, '', '错误', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644138479618', '1587622644134285314', 1, '', '正确', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644142673922', '1587622644134285314', 0, '', '错误', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644209782786', '1587622644201394178', 0, '', '正确', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644209782787', '1587622644201394178', 1, '', '错误', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644289474562', '1587622644281085953', 1, '', '正确', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644289474563', '1587622644281085953', 0, '', '错误', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644364972034', '1587622644356583425', 1, '', '青海湖', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644364972035', '1587622644356583425', 0, '', '太湖', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644364972036', '1587622644356583425', 0, '', '洞庭湖', ''); +INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644369166338', '1587622644356583425', 0, '', '鄱阳湖', ''); +COMMIT; + +-- ---------------------------- +-- Table structure for el_qu_repo +-- ---------------------------- +DROP TABLE IF EXISTS `el_qu_repo`; +CREATE TABLE `el_qu_repo` ( + `id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL, + `qu_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '试题', + `repo_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '归属题库', + `qu_type` int NOT NULL DEFAULT '0' COMMENT '题目类型', + `sort` int NOT NULL DEFAULT '0' COMMENT '排序', + PRIMARY KEY (`id`) USING BTREE, + KEY `qu_id` (`qu_id`) USING BTREE, + KEY `repo_id` (`repo_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='试题题库关联'; + +-- ---------------------------- +-- Records of el_qu_repo +-- ---------------------------- +BEGIN; +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670122192897', '1587622643760992258', '1587622451624120321', 2, 1); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670143164418', '1587622644281085953', '1587622451624120321', 3, 2); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670159941633', '1587622643626774530', '1587622451624120321', 1, 3); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670180913153', '1587622644134285314', '1587622451624120321', 3, 4); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670201884673', '1587622643979096066', '1587622451624120321', 3, 5); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670243827714', '1587622644356583425', '1587622451624120321', 1, 6); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670268993537', '1587622643693883394', '1587622451624120321', 2, 7); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670315130881', '1587622644201394178', '1587622451624120321', 3, 8); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670340296706', '1587622644054593538', '1587622451624120321', 3, 9); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670361268226', '1587622643895209985', '1587622451624120321', 2, 10); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670382239745', '1587622642641113089', '1587622451624120321', 1, 11); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670411599873', '1587622643102486530', '1587622451624120321', 1, 12); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670436765698', '1587622642473340930', '1587622451624120321', 1, 13); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670461931522', '1587622642976657410', '1587622451624120321', 3, 14); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670491291650', '1587622642263625729', '1587622451624120321', 1, 15); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670520651778', '1587622643467390977', '1587622451624120321', 1, 16); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670545817602', '1587622642838245378', '1587622451624120321', 2, 17); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670575177730', '1587622643312201730', '1587622451624120321', 1, 18); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670604537858', '1587622642699833345', '1587622451624120321', 1, 19); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670638092289', '1587622643173789698', '1587622451624120321', 1, 20); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670667452417', '1587622642557227009', '1587622451624120321', 1, 21); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670696812545', '1587622643039571970', '1587622451624120321', 1, 22); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670721978369', '1587622643551277058', '1587622451624120321', 1, 23); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670751338497', '1587622642393649153', '1587622451624120321', 1, 24); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670776504322', '1587622642909548545', '1587622451624120321', 3, 25); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670810058754', '1587622643396087810', '1587622451624120321', 1, 26); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670843613186', '1587622642762747906', '1587622451624120321', 2, 27); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670872973313', '1587622643240898561', '1587622451624120321', 1, 28); +INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1589528323552296962', '1587622643823906818', '1587622451624120321', 2, 29); +COMMIT; + +-- ---------------------------- +-- Table structure for el_repo +-- ---------------------------- +DROP TABLE IF EXISTS `el_repo`; +CREATE TABLE `el_repo` ( + `id` varchar(64) CHARACTER SET utf8 NOT NULL COMMENT '题库ID', + `code` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '题库编号', + `title` varchar(255) CHARACTER SET utf8 NOT NULL COMMENT '题库名称', + `remark` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '题库备注', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='题库信息'; + +-- ---------------------------- +-- Records of el_repo +-- ---------------------------- +BEGIN; +INSERT INTO `el_repo` (`id`, `code`, `title`, `remark`, `create_time`, `update_time`) VALUES ('1587622451624120321', '', '【云帆】演示题库', '【云帆】演示题库', '2022-11-02 09:47:44', '2022-11-02 09:47:44'); +COMMIT; + +-- ---------------------------- +-- Table structure for el_user_book +-- ---------------------------- +DROP TABLE IF EXISTS `el_user_book`; +CREATE TABLE `el_user_book` ( + `id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID', + `exam_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '考试ID', + `user_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID', + `qu_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目ID', + `create_time` datetime DEFAULT NULL COMMENT '加入时间', + `update_time` datetime DEFAULT NULL COMMENT '最近错误时间', + `wrong_count` int NOT NULL COMMENT '错误时间', + `title` varchar(1000) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目标题', + `sort` int NOT NULL COMMENT '错题序号', + PRIMARY KEY (`id`) USING BTREE, + KEY `user_id` (`user_id`), + KEY `sort` (`sort`), + KEY `exam_id` (`exam_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='错题本'; + +-- ---------------------------- +-- Records of el_user_book +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for el_user_exam +-- ---------------------------- +DROP TABLE IF EXISTS `el_user_exam`; +CREATE TABLE `el_user_exam` ( + `id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL, + `user_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID', + `exam_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '考试ID', + `try_count` int NOT NULL DEFAULT '1' COMMENT '考试次数', + `max_score` int NOT NULL DEFAULT '0' COMMENT '最高分数', + `passed` tinyint NOT NULL DEFAULT '0' COMMENT '是否通过', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `user_id` (`user_id`,`exam_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试记录'; + +-- ---------------------------- +-- Records of el_user_exam +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for QRTZ_BLOB_TRIGGERS +-- ---------------------------- +DROP TABLE IF EXISTS `QRTZ_BLOB_TRIGGERS`; +CREATE TABLE `QRTZ_BLOB_TRIGGERS` ( + `SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `BLOB_DATA` blob, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of QRTZ_BLOB_TRIGGERS +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for QRTZ_CALENDARS +-- ---------------------------- +DROP TABLE IF EXISTS `QRTZ_CALENDARS`; +CREATE TABLE `QRTZ_CALENDARS` ( + `SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL, + `CALENDAR_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `CALENDAR` blob NOT NULL, + PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of QRTZ_CALENDARS +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for QRTZ_CRON_TRIGGERS +-- ---------------------------- +DROP TABLE IF EXISTS `QRTZ_CRON_TRIGGERS`; +CREATE TABLE `QRTZ_CRON_TRIGGERS` ( + `SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `CRON_EXPRESSION` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `TIME_ZONE_ID` varchar(80) COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of QRTZ_CRON_TRIGGERS +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for QRTZ_FIRED_TRIGGERS +-- ---------------------------- +DROP TABLE IF EXISTS `QRTZ_FIRED_TRIGGERS`; +CREATE TABLE `QRTZ_FIRED_TRIGGERS` ( + `SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL, + `ENTRY_ID` varchar(95) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `INSTANCE_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `FIRED_TIME` bigint NOT NULL, + `SCHED_TIME` bigint NOT NULL, + `PRIORITY` int NOT NULL, + `STATE` varchar(16) COLLATE utf8mb4_general_ci NOT NULL, + `JOB_NAME` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL, + `JOB_GROUP` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL, + `IS_NONCONCURRENT` varchar(1) COLLATE utf8mb4_general_ci DEFAULT NULL, + `REQUESTS_RECOVERY` varchar(1) COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of QRTZ_FIRED_TRIGGERS +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for QRTZ_JOB_DETAILS +-- ---------------------------- +DROP TABLE IF EXISTS `QRTZ_JOB_DETAILS`; +CREATE TABLE `QRTZ_JOB_DETAILS` ( + `SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL, + `JOB_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `JOB_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `DESCRIPTION` varchar(250) COLLATE utf8mb4_general_ci DEFAULT NULL, + `JOB_CLASS_NAME` varchar(250) COLLATE utf8mb4_general_ci NOT NULL, + `IS_DURABLE` varchar(1) COLLATE utf8mb4_general_ci NOT NULL, + `IS_NONCONCURRENT` varchar(1) COLLATE utf8mb4_general_ci NOT NULL, + `IS_UPDATE_DATA` varchar(1) COLLATE utf8mb4_general_ci NOT NULL, + `REQUESTS_RECOVERY` varchar(1) COLLATE utf8mb4_general_ci NOT NULL, + `JOB_DATA` blob, + PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of QRTZ_JOB_DETAILS +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for QRTZ_LOCKS +-- ---------------------------- +DROP TABLE IF EXISTS `QRTZ_LOCKS`; +CREATE TABLE `QRTZ_LOCKS` ( + `SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL, + `LOCK_NAME` varchar(40) COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of QRTZ_LOCKS +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for QRTZ_PAUSED_TRIGGER_GRPS +-- ---------------------------- +DROP TABLE IF EXISTS `QRTZ_PAUSED_TRIGGER_GRPS`; +CREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` ( + `SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of QRTZ_PAUSED_TRIGGER_GRPS +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for QRTZ_SCHEDULER_STATE +-- ---------------------------- +DROP TABLE IF EXISTS `QRTZ_SCHEDULER_STATE`; +CREATE TABLE `QRTZ_SCHEDULER_STATE` ( + `SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL, + `INSTANCE_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `LAST_CHECKIN_TIME` bigint NOT NULL, + `CHECKIN_INTERVAL` bigint NOT NULL, + PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of QRTZ_SCHEDULER_STATE +-- ---------------------------- +BEGIN; +INSERT INTO `QRTZ_SCHEDULER_STATE` (`SCHED_NAME`, `INSTANCE_NAME`, `LAST_CHECKIN_TIME`, `CHECKIN_INTERVAL`) VALUES ('examScheduler', 'MacBook-Pro-16.local1676860344454', 1676860726631, 10000); +COMMIT; + +-- ---------------------------- +-- Table structure for QRTZ_SIMPLE_TRIGGERS +-- ---------------------------- +DROP TABLE IF EXISTS `QRTZ_SIMPLE_TRIGGERS`; +CREATE TABLE `QRTZ_SIMPLE_TRIGGERS` ( + `SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `REPEAT_COUNT` bigint NOT NULL, + `REPEAT_INTERVAL` bigint NOT NULL, + `TIMES_TRIGGERED` bigint NOT NULL, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of QRTZ_SIMPLE_TRIGGERS +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for QRTZ_SIMPROP_TRIGGERS +-- ---------------------------- +DROP TABLE IF EXISTS `QRTZ_SIMPROP_TRIGGERS`; +CREATE TABLE `QRTZ_SIMPROP_TRIGGERS` ( + `SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `STR_PROP_1` varchar(512) COLLATE utf8mb4_general_ci DEFAULT NULL, + `STR_PROP_2` varchar(512) COLLATE utf8mb4_general_ci DEFAULT NULL, + `STR_PROP_3` varchar(512) COLLATE utf8mb4_general_ci DEFAULT NULL, + `INT_PROP_1` int DEFAULT NULL, + `INT_PROP_2` int DEFAULT NULL, + `LONG_PROP_1` bigint DEFAULT NULL, + `LONG_PROP_2` bigint DEFAULT NULL, + `DEC_PROP_1` decimal(13,4) DEFAULT NULL, + `DEC_PROP_2` decimal(13,4) DEFAULT NULL, + `BOOL_PROP_1` varchar(1) COLLATE utf8mb4_general_ci DEFAULT NULL, + `BOOL_PROP_2` varchar(1) COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of QRTZ_SIMPROP_TRIGGERS +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for QRTZ_TRIGGERS +-- ---------------------------- +DROP TABLE IF EXISTS `QRTZ_TRIGGERS`; +CREATE TABLE `QRTZ_TRIGGERS` ( + `SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `JOB_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `JOB_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL, + `DESCRIPTION` varchar(250) COLLATE utf8mb4_general_ci DEFAULT NULL, + `NEXT_FIRE_TIME` bigint DEFAULT NULL, + `PREV_FIRE_TIME` bigint DEFAULT NULL, + `PRIORITY` int DEFAULT NULL, + `TRIGGER_STATE` varchar(16) COLLATE utf8mb4_general_ci NOT NULL, + `TRIGGER_TYPE` varchar(8) COLLATE utf8mb4_general_ci NOT NULL, + `START_TIME` bigint NOT NULL, + `END_TIME` bigint DEFAULT NULL, + `CALENDAR_NAME` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL, + `MISFIRE_INSTR` smallint DEFAULT NULL, + `JOB_DATA` blob, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + KEY `SCHED_NAME` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`), + CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of QRTZ_TRIGGERS +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for sys_config +-- ---------------------------- +DROP TABLE IF EXISTS `sys_config`; +CREATE TABLE `sys_config` ( + `id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID', + `site_name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '系统名称', + `front_logo` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '前端LOGO', + `back_logo` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '后台LOGO', + `copy_right` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '版权信息', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `create_by` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建人', + `update_by` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '修改人', + `data_flag` int DEFAULT '0' COMMENT '数据标识', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统设置'; + +-- ---------------------------- +-- Records of sys_config +-- ---------------------------- +BEGIN; +INSERT INTO `sys_config` (`id`, `site_name`, `front_logo`, `back_logo`, `copy_right`, `create_time`, `update_time`, `create_by`, `update_by`, `data_flag`) VALUES ('1', '云帆在线培训考试系统', NULL, NULL, NULL, '2020-12-03 16:51:30', '2020-12-03 16:51:30', '', '', 1); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_depart +-- ---------------------------- +DROP TABLE IF EXISTS `sys_depart`; +CREATE TABLE `sys_depart` ( + `id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID', + `dept_type` int NOT NULL DEFAULT '1' COMMENT '1公司2部门', + `parent_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '所属上级', + `dept_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '部门名称', + `dept_code` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '部门编码', + `sort` int NOT NULL DEFAULT '0' COMMENT '排序', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='部门信息'; + +-- ---------------------------- +-- Records of sys_depart +-- ---------------------------- +BEGIN; +INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302853644578000898', 1, '0', '北京云帆互联科技有限公司', 'A01', 1); +INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302855940200284161', 1, '1302855776496599041', '后端组', 'A01A01A01', 2); +INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302855994843676674', 1, '1302855776496599041', '前端组', 'A01A01A02', 1); +INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302856017283203073', 1, '1302855776496599041', '产品组', 'A01A01A03', 3); +INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302856084475953154', 1, '1302855776496599041', '测试组', 'A01A01A05', 5); +INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302856266567467010', 1, '1302855896415944705', '客户一组', 'A01A05A01', 1); +INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302856320602685442', 1, '1302855896415944705', '客服二组', 'A01A05A02', 2); +INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1318103313740320770', 1, '1302853644578000898', '技术部', 'A01A01', 1); +INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1318103339229106178', 1, '1302853644578000898', '人事部', 'A01A02', 2); +INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1318103362494910465', 1, '1302853644578000898', '财务部', 'A01A03', 3); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_role +-- ---------------------------- +DROP TABLE IF EXISTS `sys_role`; +CREATE TABLE `sys_role` ( + `id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色ID', + `role_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '角色名称', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='角色'; + +-- ---------------------------- +-- Records of sys_role +-- ---------------------------- +BEGIN; +INSERT INTO `sys_role` (`id`, `role_name`) VALUES ('sa', '超级管理员'); +INSERT INTO `sys_role` (`id`, `role_name`) VALUES ('student', '学员'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_user +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user`; +CREATE TABLE `sys_user` ( + `id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID', + `user_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名', + `real_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '真实姓名', + `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '密码', + `salt` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '密码盐', + `role_ids` varchar(500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '角色列表', + `depart_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '部门ID', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `state` int NOT NULL DEFAULT '0' COMMENT '状态', + `data_flag` int NOT NULL DEFAULT '0' COMMENT '0正常,1隐藏', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='管理用户'; + +-- ---------------------------- +-- Records of sys_user +-- ---------------------------- +BEGIN; +INSERT INTO `sys_user` (`id`, `user_name`, `real_name`, `password`, `salt`, `role_ids`, `depart_id`, `create_time`, `update_time`, `state`, `data_flag`) VALUES ('10001', 'admin', '超管A', '06681cd08837b21adf6b5ef9279d403d', 'XoFFuS', 'sa', '1318103313740320770', '2020-04-20 13:51:03', '2020-04-20 13:51:03', 0, 0); +INSERT INTO `sys_user` (`id`, `user_name`, `real_name`, `password`, `salt`, `role_ids`, `depart_id`, `create_time`, `update_time`, `state`, `data_flag`) VALUES ('1252125239901696002', 'person', '张三', '6dfdd6761a3e8319719f32abb9aeae9c', 'tZCjLq', 'student', '1318103339229106178', '2020-04-20 14:41:35', '2020-04-20 14:41:35', 0, 0); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_user_role +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user_role`; +CREATE TABLE `sys_user_role` ( + `id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID', + `user_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID', + `role_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色ID', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户角色'; + +-- ---------------------------- +-- Records of sys_user_role +-- ---------------------------- +BEGIN; +INSERT INTO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES ('1318103579445284865', '10001', 'sa'); +INSERT INTO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES ('1318128865264132097', '1252125239901696002', 'student'); +INSERT INTO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES ('1587574421424279555', '1587574421424279554', 'student'); +COMMIT; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/docs/数据表结构.pdf b/docs/数据表结构.pdf new file mode 100644 index 0000000..501b767 Binary files /dev/null and b/docs/数据表结构.pdf differ diff --git a/docs/注册Windows服务/ReadMe.txt b/docs/注册Windows服务/ReadMe.txt new file mode 100644 index 0000000..4ae1b21 --- /dev/null +++ b/docs/注册Windows服务/ReadMe.txt @@ -0,0 +1,4 @@ +1、把运行包中的exam-api.jar和application-local.yml复制到本目录 +2、运行命令注册为Windows系统服务:yf-exam.exe install +3、在服务中即可找到名称为:CloudExam的服务,启动即可,告别黑窗烦恼 +注:本功能适合.NetFramework 4.0版本,如果无法注册,请检查环境是否匹配 \ No newline at end of file diff --git a/docs/注册Windows服务/yf-exam.exe b/docs/注册Windows服务/yf-exam.exe new file mode 100644 index 0000000..ff09693 Binary files /dev/null and b/docs/注册Windows服务/yf-exam.exe differ diff --git a/docs/注册Windows服务/yf-exam.xml b/docs/注册Windows服务/yf-exam.xml new file mode 100644 index 0000000..6b13039 --- /dev/null +++ b/docs/注册Windows服务/yf-exam.xml @@ -0,0 +1,8 @@ + + yf-exam + CloudExam + 云帆在线考试系统服务,运行在http://localhost:8101 + java + -jar exam-api.jar --spring.config.location=application-local.yml + + diff --git a/docs/源码说明.pdf b/docs/源码说明.pdf new file mode 100644 index 0000000..8bde648 Binary files /dev/null and b/docs/源码说明.pdf differ diff --git a/docs/运行包/application-local.yml b/docs/运行包/application-local.yml new file mode 100644 index 0000000..00b1fcb --- /dev/null +++ b/docs/运行包/application-local.yml @@ -0,0 +1,85 @@ +# 独立配置文件,可以拿到jar外面跑 +spring: + application: + name: yf-exam-lite + profiles: + active: dev + main: + allow-bean-definition-overriding: true + # 数据库配置 + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/yf_exam?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true + username: root + password: root + # druid相关配置 + druid: + max-active: 5000 + initial-size: 20 + min-idle: 5 + async-init: true + # 监控统计 + filters: stat,wall + filter: + stat: + log-slow-sql: true + slow-sql-millis: 5000 + wall: + config: + create-table-allow: false + alter-table-allow: false + drop-table-allow: false + truncate-allow: false + + # 定时任务配置 + quartz: + # 数据库方式 + job-store-type: jdbc + # quartz 相关属性配置 + properties: + org: + quartz: + scheduler: + instanceName: examScheduler + instanceId: AUTO + jobStore: + class: org.quartz.impl.jdbcjobstore.JobStoreTX + driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate + tablePrefix: QRTZ_ + isClustered: true + clusterCheckinInterval: 10000 + useProperties: false + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 10 + threadPriority: 5 + threadsInheritContextClassLoaderOfInitializingThread: true + +server: + port: 8101 + # 启用服务端压缩 + compression: + enabled: true + min-response-size: 10 + mime-types: application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css + +# 文件上传配置 +conf: + upload: + # 物理文件存储位置,以/结束,windows已正斜杠,如:d:/exam-upload/ + dir: /data/upload/ + # 访问地址,注意不要去除/upload/file/,此节点为虚拟标识符 + # 如:http://localhost:8101/upload/file/exam.jpg,对应物理文件为:/data/upload/exam.jpg + url: http://localhost:8101/upload/file/ + # 允许上传的文件后缀 + allow-extensions: jpg,jpeg,png + +# 开启文档 +swagger: + enable: true + +logging: + level: + root: debug + path: logs/${spring.application.name}/ diff --git a/docs/运行包/exam-api.jar b/docs/运行包/exam-api.jar new file mode 100644 index 0000000..248afcf Binary files /dev/null and b/docs/运行包/exam-api.jar differ diff --git a/docs/运行包/start.bat b/docs/运行包/start.bat new file mode 100644 index 0000000..f2b3be2 --- /dev/null +++ b/docs/运行包/start.bat @@ -0,0 +1 @@ +java -jar exam-api.jar --spring.config.location=application-local.yml \ No newline at end of file diff --git a/docs/运行包/start.sh b/docs/运行包/start.sh new file mode 100644 index 0000000..f2b3be2 --- /dev/null +++ b/docs/运行包/start.sh @@ -0,0 +1 @@ +java -jar exam-api.jar --spring.config.location=application-local.yml \ No newline at end of file diff --git a/docs/运行包/运行说明.txt b/docs/运行包/运行说明.txt new file mode 100644 index 0000000..5a23852 --- /dev/null +++ b/docs/运行包/运行说明.txt @@ -0,0 +1,7 @@ +1、下载编译好的jar包到本目录(或您自行编译):https://cdn.yfhl.net/lite/exam-api.jar +2、自行安装MySQL数据库(版本最好是5.7),将`安装资源中`的`数据库初始化.sql`导入到安装好的数据库 +3、安装Java环境,要求JDK版本大于1.8 +4、请修改外置配置文件:application-local.yml 改成您自己的MySQL配置 +5、Windows通过start.bat运行,Linux运行start.sh运行 +6、如果无意外,可通过:http://localhost:8101 访问到项目了 +7、管理员账号密码:admin/admin 学员账号:person/person \ No newline at end of file diff --git a/docs/部署手册.pdf b/docs/部署手册.pdf new file mode 100644 index 0000000..308137b Binary files /dev/null and b/docs/部署手册.pdf differ diff --git a/exam-api/pom.xml b/exam-api/pom.xml new file mode 100644 index 0000000..75217d5 --- /dev/null +++ b/exam-api/pom.xml @@ -0,0 +1,215 @@ + + 4.0.0 + com.yfhl + exam-api + jar + 1.0 + exam-api + + + org.springframework.boot + spring-boot-starter-parent + 2.1.4.RELEASE + + + + 2.0.24 + 3.7.0 + 4.1.1 + 2.9.2 + 5.5.1 + 3.8 + 8.0.11 + 3.4.1 + 1.18.4 + 3.0.11.RELEASE + 2.1.1.RELEASE + 3.9 + 2.17.2 + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-quartz + + + + org.aspectj + aspectjweaver + 1.9.5 + + + + com.alibaba + fastjson + ${fastjson.version} + + + + net.sf.dozer + dozer + ${dozer.version} + + + commons-collections + commons-collections + + + org.slf4j + slf4j-api + + + + + + + + + com.baomidou + mybatis-plus + ${mybatis-plus.version} + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + mysql + mysql-connector-java + ${mysql.driver.version} + + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + org.dom4j + dom4j + 2.1.1 + + + + io.springfox + springfox-swagger2 + ${swagger.version} + + + org.slf4j + slf4j-api + + + + + + com.github.xiaoymin + swagger-bootstrap-ui + 1.9.3 + + + + + org.apache.poi + poi + ${poi.version} + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + org.apache.poi + poi-ooxml-schemas + ${poi.version} + + + + + com.auth0 + java-jwt + 3.7.0 + + + + + org.apache.shiro + shiro-spring-boot-starter + 1.8.0 + + + + + com.alibaba + druid-spring-boot-starter + 1.2.6 + + + + commons-io + commons-io + 2.11.0 + + + + + + + ${project.name} + compile + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + UTF-8 + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.1 + + true + + + + + + + diff --git a/exam-api/src/main/java/com/yf/exam/ExamApplication.java b/exam-api/src/main/java/com/yf/exam/ExamApplication.java new file mode 100644 index 0000000..265c269 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ExamApplication.java @@ -0,0 +1,53 @@ +package com.yf.exam; + +import com.yf.exam.core.api.utils.JsonConverter; +import lombok.extern.log4j.Log4j2; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; + +/** + * 云帆在线考试系统 + * @author bool + * @email 18365918@qq.com + * @date 2020-03-04 19:41 + */ +@Log4j2 +@SpringBootApplication +public class ExamApplication implements WebMvcConfigurer { + + public static void main(String[] args) throws UnknownHostException { + ConfigurableApplicationContext application = SpringApplication.run(ExamApplication.class, args); + Environment env = application.getEnvironment(); + String ip = InetAddress.getLocalHost().getHostAddress(); + String port = env.getProperty("server.port"); + String path = env.getProperty("server.servlet.context-path"); + + // 未配置默认空白 + if(path == null){ + path = ""; + } + + + log.info("\n----------------------------------------------------------\n\t" + + "云帆考试系统启动成功,访问路径如下:\n\t" + + "本地路径: \t\thttp://localhost:" + port + path + "/\n\t" + + "网络地址: \thttp://" + ip + ":" + port + path + "/\n\t" + + "API文档: \t\thttp://" + ip + ":" + port + path + "/doc.html\n" + + "----------------------------------------------------------"); + } + + @Override + public void extendMessageConverters(List> converters) { + //保留原有converter,把新增fastConverter插入集合头,保证优先级 + converters.add(0, JsonConverter.fastConverter()); + } + +} \ No newline at end of file diff --git a/exam-api/src/main/java/com/yf/exam/ability/Constant.java b/exam-api/src/main/java/com/yf/exam/ability/Constant.java new file mode 100644 index 0000000..9880ea6 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/Constant.java @@ -0,0 +1,15 @@ +package com.yf.exam.ability; + + +/** + * 通用常量 + * @author bool + */ +public class Constant { + + + /** + * 文件上传路径 + */ + public static final String FILE_PREFIX = "/upload/file/"; +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/job/enums/JobGroup.java b/exam-api/src/main/java/com/yf/exam/ability/job/enums/JobGroup.java new file mode 100644 index 0000000..2159361 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/job/enums/JobGroup.java @@ -0,0 +1,13 @@ +package com.yf.exam.ability.job.enums; + +/** + * 任务分组 + * @author van + */ +public interface JobGroup { + + /** + * 系统任务 + */ + String SYSTEM = "system"; +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/job/enums/JobPrefix.java b/exam-api/src/main/java/com/yf/exam/ability/job/enums/JobPrefix.java new file mode 100644 index 0000000..2536f0e --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/job/enums/JobPrefix.java @@ -0,0 +1,14 @@ +package com.yf.exam.ability.job.enums; + +/** + * 任务前缀 + * @author bool + */ +public interface JobPrefix { + + /** + * 强制交卷的 + */ + String BREAK_EXAM = "break_exam_"; + +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/job/service/JobService.java b/exam-api/src/main/java/com/yf/exam/ability/job/service/JobService.java new file mode 100644 index 0000000..cb465de --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/job/service/JobService.java @@ -0,0 +1,53 @@ +package com.yf.exam.ability.job.service; + +/** + * 任务业务类,用于动态处理任务信息 + * @author bool + * @date 2020/11/29 下午2:17 + */ +public interface JobService { + + + /** + * 任务数据 + */ + String TASK_DATA = "taskData"; + + /** + * 添加定时任务 + * @param jobClass + * @param jobName + * @param cron + * @param data + */ + void addCronJob(Class jobClass, String jobName, String cron, String data); + + /** + * 添加立即执行的任务 + * @param jobClass + * @param jobName + * @param data + */ + void addCronJob(Class jobClass, String jobName, String data); + + /** + * 暂停任务 + * @param jobName + * @param jobGroup + */ + void pauseJob(String jobName, String jobGroup); + + /** + * 恢复任务 + * @param triggerName + * @param triggerGroup + */ + void resumeJob(String triggerName, String triggerGroup); + + /** + * 删除job + * @param jobName + * @param jobGroup + */ + void deleteJob(String jobName, String jobGroup); +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/job/service/impl/JobServiceImpl.java b/exam-api/src/main/java/com/yf/exam/ability/job/service/impl/JobServiceImpl.java new file mode 100644 index 0000000..aafdfdb --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/job/service/impl/JobServiceImpl.java @@ -0,0 +1,123 @@ +package com.yf.exam.ability.job.service.impl; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.yf.exam.ability.job.enums.JobGroup; +import com.yf.exam.ability.job.service.JobService; +import lombok.extern.log4j.Log4j2; +import org.quartz.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +/** + * @author bool + */ +@Log4j2 +@Service +public class JobServiceImpl implements JobService { + + /** + * Quartz定时任务核心的功能实现类 + */ + private Scheduler scheduler; + + /** + * 注入 + * @param schedulerFactoryBean + */ + public JobServiceImpl(@Autowired SchedulerFactoryBean schedulerFactoryBean) { + scheduler = schedulerFactoryBean.getScheduler(); + } + + + @Override + public void addCronJob(Class jobClass, String jobName, String cron, String data) { + + + String jobGroup = JobGroup.SYSTEM; + + // 自动命名 + if(StringUtils.isEmpty(jobName)){ + jobName = jobClass.getSimpleName().toUpperCase() + "_"+IdWorker.getIdStr(); + } + + try { + JobKey jobKey = JobKey.jobKey(jobName, jobGroup); + JobDetail jobDetail = scheduler.getJobDetail(jobKey); + if (jobDetail != null) { + log.info("++++++++++任务:{} 已存在", jobName); + this.deleteJob(jobName, jobGroup); + } + + log.info("++++++++++构建任务:{},{},{},{},{} ", jobClass.toString(), jobName, jobGroup, cron, data); + + //构建job信息 + jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build(); + //用JopDataMap来传递数据 + jobDetail.getJobDataMap().put(TASK_DATA, data); + + //按新的cronExpression表达式构建一个新的trigger + Trigger trigger = null; + + // 有表达式的按表达式 + if(!StringUtils.isEmpty(cron)){ + log.info("+++++表达式执行:"+ JSON.toJSONString(jobDetail)); + //表达式调度构建器 + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron); + trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build(); + }else{ + // 无表达式则立即执行 + log.info("+++++立即执行:"+ JSON.toJSONString(jobDetail)); + trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).startNow().build(); + } + + scheduler.scheduleJob(jobDetail, trigger); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + + @Override + public void addCronJob(Class jobClass, String jobName, String data) { + // 立即执行任务 + this.addCronJob(jobClass, jobName, null, data); + } + + + @Override + public void pauseJob(String jobName, String jobGroup) { + try { + TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); + scheduler.pauseTrigger(triggerKey); + log.info("++++++++++暂停任务:{}", jobName); + } catch (SchedulerException e) { + e.printStackTrace(); + } + } + + @Override + public void resumeJob(String jobName, String jobGroup) { + try { + TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); + scheduler.resumeTrigger(triggerKey); + log.info("++++++++++重启任务:{}", jobName); + } catch (SchedulerException e) { + e.printStackTrace(); + } + } + + @Override + public void deleteJob(String jobName, String jobGroup) { + try { + JobKey jobKey = JobKey.jobKey(jobName,jobGroup); + scheduler.deleteJob(jobKey); + log.info("++++++++++删除任务:{}", jobKey); + } catch (SchedulerException e) { + e.printStackTrace(); + } + } +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/shiro/CNFilterFactoryBean.java b/exam-api/src/main/java/com/yf/exam/ability/shiro/CNFilterFactoryBean.java new file mode 100644 index 0000000..3bc2190 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/shiro/CNFilterFactoryBean.java @@ -0,0 +1,29 @@ +package com.yf.exam.ability.shiro; + +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.filter.InvalidRequestFilter; +import org.apache.shiro.web.filter.mgt.DefaultFilter; +import org.apache.shiro.web.filter.mgt.FilterChainManager; + +import javax.servlet.Filter; +import java.util.Map; + +/** + * 自定义过滤器,用于处理中文URL问题 + * 如:下载文件中包含中文会返回400错误,https://youdomain.com/upload/file/云帆考试系统用户手册.pdf + * @author van + */ +public class CNFilterFactoryBean extends ShiroFilterFactoryBean { + + @Override + protected FilterChainManager createFilterChainManager() { + FilterChainManager manager = super.createFilterChainManager(); + // URL携带中文400,servletPath中文校验bug + Map filterMap = manager.getFilters(); + Filter invalidRequestFilter = filterMap.get(DefaultFilter.invalidRequest.name()); + if (invalidRequestFilter instanceof InvalidRequestFilter) { + ((InvalidRequestFilter) invalidRequestFilter).setBlockNonAscii(false); + } + return manager; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/shiro/ShiroRealm.java b/exam-api/src/main/java/com/yf/exam/ability/shiro/ShiroRealm.java new file mode 100644 index 0000000..76af5c5 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/shiro/ShiroRealm.java @@ -0,0 +1,131 @@ +package com.yf.exam.ability.shiro; + + +import com.yf.exam.ability.shiro.jwt.JwtToken; +import com.yf.exam.ability.shiro.jwt.JwtUtils; +import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO; +import com.yf.exam.modules.sys.user.service.SysUserRoleService; +import com.yf.exam.modules.sys.user.service.SysUserService; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.List; + +/** + * 用户登录鉴权和获取用户授权 + * @author bool + */ +@Component +@Slf4j +public class ShiroRealm extends AuthorizingRealm { + + @Autowired + @Lazy + private SysUserService sysUserService; + + @Autowired + @Lazy + private SysUserRoleService sysUserRoleService; + + + @Override + public boolean supports(AuthenticationToken token) { + return token instanceof JwtToken; + } + + + /** + * 详细授权认证 + * @param principals + * @return + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + + String userId = null; + if (principals != null) { + SysUserLoginDTO user = (SysUserLoginDTO) principals.getPrimaryPrincipal(); + userId = user.getId(); + } + SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); + + // 查找用户角色 + List roles = sysUserRoleService.listRoles(userId); + info.setRoles(new HashSet<>(roles)); + + log.info("++++++++++校验详细权限完成"); + return info; + } + + /** + * 校验用户的账号密码是否正确 + * @param auth + * @return + * @throws AuthenticationException + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException { + String token = (String) auth.getCredentials(); + if (token == null) { + throw new AuthenticationException("token为空!"); + } + + // 校验token有效性 + SysUserLoginDTO user = this.checkToken(token); + return new SimpleAuthenticationInfo(user, token, getName()); + } + + + /** + * 校验Token的有效性 + * @param token + * @return + * @throws AuthenticationException + */ + public SysUserLoginDTO checkToken(String token) throws AuthenticationException { + + // 查询用户信息 + log.debug("++++++++++校验用户token: "+ token); + + // 从token中获取用户名 + String username = JwtUtils.getUsername(token); + log.debug("++++++++++用户名: "+ username); + + if (username == null) { + throw new AuthenticationException("无效的token"); + } + + // 查找登录用户对象 + SysUserLoginDTO user = sysUserService.token(token); + + // 校验token是否失效 + if (!JwtUtils.verify(token, username)) { + throw new AuthenticationException("登陆失效,请重试登陆!"); + } + + return user; + } + + + + /** + * 清除当前用户的权限认证缓存 + * @param principals + */ + @Override + public void clearCache(PrincipalCollection principals) { + super.clearCache(principals); + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/shiro/aop/JwtFilter.java b/exam-api/src/main/java/com/yf/exam/ability/shiro/aop/JwtFilter.java new file mode 100644 index 0000000..88cf448 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/shiro/aop/JwtFilter.java @@ -0,0 +1,53 @@ +package com.yf.exam.ability.shiro.aop; + + +import com.yf.exam.ability.shiro.jwt.JwtToken; +import com.yf.exam.aspect.utils.InjectUtils; +import com.yf.exam.modules.Constant; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 鉴权登录拦截器 + * @author bool + */ +@Slf4j +public class JwtFilter extends BasicHttpAuthenticationFilter { + + /** + * 执行登录认证 + * @param request + * @param response + * @param mappedValue + * @return + */ + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + try { + executeLogin(request, response); + return true; + } catch (Exception e) { + // 写出统一错误信息 + InjectUtils.restError((HttpServletResponse) response); + return false; + } + } + + + @Override + protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + String token = httpServletRequest.getHeader(Constant.TOKEN); + + JwtToken jwtToken = new JwtToken(token); + // 提交给realm进行登入,如果错误他会抛出异常并被捕获 + getSubject(request, response).login(jwtToken); + // 如果没有抛出异常则代表登入成功,返回true + return true; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/shiro/jwt/JwtToken.java b/exam-api/src/main/java/com/yf/exam/ability/shiro/jwt/JwtToken.java new file mode 100644 index 0000000..d5baab3 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/shiro/jwt/JwtToken.java @@ -0,0 +1,33 @@ +package com.yf.exam.ability.shiro.jwt; + +import lombok.Data; +import org.apache.shiro.authc.AuthenticationToken; + +/** + * @author bool + */ +@Data +public class JwtToken implements AuthenticationToken { + + private static final long serialVersionUID = 1L; + + /** + * JWT的字符token + */ + private String token; + + + public JwtToken(String token) { + this.token = token; + } + + @Override + public Object getPrincipal() { + return token; + } + + @Override + public Object getCredentials() { + return token; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/shiro/jwt/JwtUtils.java b/exam-api/src/main/java/com/yf/exam/ability/shiro/jwt/JwtUtils.java new file mode 100644 index 0000000..4a66759 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/shiro/jwt/JwtUtils.java @@ -0,0 +1,99 @@ +package com.yf.exam.ability.shiro.jwt; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.yf.exam.core.utils.file.Md5Util; + +import java.util.Calendar; +import java.util.Date; + +/** + * JWT工具类 + * @author bool + */ +public class JwtUtils { + + /** + * 有效期24小时 + */ + private static final long EXPIRE_TIME = 24 * 60 * 60 * 1000; + + + /** + * 校验是否正确 + * @param token + * @param username + * @return + */ + public static boolean verify(String token, String username) { + try { + // 根据密码生成JWT效验器 + Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username)); + JWTVerifier verifier = JWT.require(algorithm) + .withClaim("username", username) + .build(); + // 效验TOKEN + verifier.verify(token); + return true; + } catch (Exception exception) { + return false; + } + } + + + + + + /** + * 从Token中解密获得用户名 + * @param token + * @return + */ + public static String getUsername(String token) { + try { + DecodedJWT jwt = JWT.decode(token); + return jwt.getClaim("username").asString(); + } catch (JWTDecodeException e) { + return null; + } + } + + /** + * 生成JWT Token字符串 + * @param username + * @return + */ + public static String sign(String username) { + Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); + Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username)); + // 附带username信息 + return JWT.create() + .withClaim("username", username) + .withExpiresAt(date).sign(algorithm); + + } + + /** + * 根据用户名和秘钥,生成一个新的秘钥,用于JWT加强一些安全性 + * @param userName + * @return + */ + private static String encryptSecret(String userName){ + + // 一个简单的登录规则,用户名+当前月份为加密串,意思每个月会变,要重新登录 + // 可自行修改此规则 + Calendar cl = Calendar.getInstance(); + cl.setTimeInMillis(System.currentTimeMillis()); + StringBuffer sb = new StringBuffer(userName) + .append("&") + .append(cl.get(Calendar.MONTH)); + + // 获取MD5 + String secret = Md5Util.md5(sb.toString()); + + return Md5Util.md5(userName + "&" + secret); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/upload/config/UploadConfig.java b/exam-api/src/main/java/com/yf/exam/ability/upload/config/UploadConfig.java new file mode 100644 index 0000000..e35d73d --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/upload/config/UploadConfig.java @@ -0,0 +1,32 @@ +package com.yf.exam.ability.upload.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + + +/** + * 文件上传配置 + * @author van + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "conf.upload") +public class UploadConfig { + + /** + * 访问路径 + */ + private String url; + + /** + * 物理目录 + */ + private String dir; + + /** + * 允许的后缀 + */ + private String [] allowExtensions; + +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/upload/controller/UploadController.java b/exam-api/src/main/java/com/yf/exam/ability/upload/controller/UploadController.java new file mode 100644 index 0000000..4c85250 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/upload/controller/UploadController.java @@ -0,0 +1,57 @@ +package com.yf.exam.ability.upload.controller; + + +import com.yf.exam.ability.Constant; +import com.yf.exam.ability.upload.dto.UploadReqDTO; +import com.yf.exam.ability.upload.dto.UploadRespDTO; +import com.yf.exam.ability.upload.service.UploadService; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 本地文件上传下载请求类 + * @author bool + */ +@Log4j2 +@Api(tags = {"文件上传"}) +@RestController +public class UploadController extends BaseController { + + @Autowired + private UploadService uploadService; + + /** + * 文件上传 + * @param reqDTO + * @return + */ + @PostMapping("/common/api/file/upload") + @ApiOperation(value = "文件上传", notes = "此接口较为特殊,参数都通过表单方式提交,而非JSON") + public ApiRest upload(@ModelAttribute UploadReqDTO reqDTO) { + // 上传并返回URL + UploadRespDTO respDTO = uploadService.upload(reqDTO); + return super.success(respDTO); + } + + /** + * 独立文件下载 + * @param request + * @param response + */ + @GetMapping(Constant.FILE_PREFIX+"**") + @ApiOperation(value = "文件下载", notes = "文件下载") + public void download(HttpServletRequest request, HttpServletResponse response) { + uploadService.download(request, response); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/upload/dto/UploadReqDTO.java b/exam-api/src/main/java/com/yf/exam/ability/upload/dto/UploadReqDTO.java new file mode 100644 index 0000000..df2f286 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/upload/dto/UploadReqDTO.java @@ -0,0 +1,22 @@ +package com.yf.exam.ability.upload.dto; + + +import com.yf.exam.core.api.dto.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件上传请求类 + * @author + * @date 2019-12-26 17:54 + */ +@Data +@ApiModel(value="文件上传参数", description="文件上传参数") +public class UploadReqDTO extends BaseDTO { + + @ApiModelProperty(value = "上传文件内容", required=true) + private MultipartFile file; + +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/upload/dto/UploadRespDTO.java b/exam-api/src/main/java/com/yf/exam/ability/upload/dto/UploadRespDTO.java new file mode 100644 index 0000000..b91106e --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/upload/dto/UploadRespDTO.java @@ -0,0 +1,23 @@ +package com.yf.exam.ability.upload.dto; + +import com.yf.exam.core.api.dto.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 上传文件结果 + * @author bool + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@ApiModel(value="文件上传响应", description="文件上传响应") +public class UploadRespDTO extends BaseDTO { + + @ApiModelProperty(value = "上传后的完整的URL地址", required=true) + private String url; + +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/upload/service/UploadService.java b/exam-api/src/main/java/com/yf/exam/ability/upload/service/UploadService.java new file mode 100644 index 0000000..ef516ec --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/upload/service/UploadService.java @@ -0,0 +1,30 @@ +package com.yf.exam.ability.upload.service; + +import com.yf.exam.ability.upload.dto.UploadReqDTO; +import com.yf.exam.ability.upload.dto.UploadRespDTO; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 阿里云OSS业务类 + * @author bool + * @date 2019-07-12 16:45 + */ +public interface UploadService { + + /** + * 文件上传 + * @param reqDTO + * @return + */ + UploadRespDTO upload(UploadReqDTO reqDTO); + + /** + * 下载文件 + * @param request + * @param response + */ + void download(HttpServletRequest request, HttpServletResponse response); + +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/upload/service/impl/UploadServiceImpl.java b/exam-api/src/main/java/com/yf/exam/ability/upload/service/impl/UploadServiceImpl.java new file mode 100644 index 0000000..ef70f40 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/upload/service/impl/UploadServiceImpl.java @@ -0,0 +1,135 @@ +package com.yf.exam.ability.upload.service.impl; + +import com.yf.exam.ability.Constant; +import com.yf.exam.ability.upload.config.UploadConfig; +import com.yf.exam.ability.upload.dto.UploadReqDTO; +import com.yf.exam.ability.upload.dto.UploadRespDTO; +import com.yf.exam.ability.upload.service.UploadService; +import com.yf.exam.ability.upload.utils.FileUtils; +import com.yf.exam.core.exception.ServiceException; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.io.FilenameUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/** + * 文件上传业务类 + * @author bool + * @date 2019-07-30 21:02 + */ +@Log4j2 +@Service +public class UploadServiceImpl implements UploadService { + + @Autowired + private UploadConfig conf; + + @Override + public UploadRespDTO upload(UploadReqDTO reqDTO) { + + + // 文件内容 + MultipartFile file = reqDTO.getFile(); + + // 验证文件后缀 + boolean allow = FilenameUtils.isExtension(file.getOriginalFilename(), conf.getAllowExtensions()); + if(!allow){ + throw new ServiceException("文件类型不允许上传!"); + } + // 上传文件夹 + String fileDir = conf.getDir(); + // 真实物理地址 + String fullPath; + try { + + // 新文件 + String filePath = FileUtils.processPath(file); + // 文件保存地址 + fullPath = fileDir + filePath; + // 创建文件夹 + FileUtils.checkDir(fullPath); + // 上传文件 + FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(fullPath)); + + return this.generateResult(filePath); + + } catch (IOException e) { + e.printStackTrace(); + throw new ServiceException("文件上传失败:"+e.getMessage()); + } + } + + + + @Override + public void download(HttpServletRequest request, HttpServletResponse response) { + + // 获取真实的文件路径 + String filePath = this.getRealPath(request.getRequestURI()); + + // 处理中文问题 + try { + filePath = URLDecoder.decode(filePath, "utf-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + + System.out.println("++++完整路径为:"+filePath); + + try { + FileUtils.writeRange(request, response, filePath); + } catch (IOException e) { + response.setStatus(404); + log.error("预览文件失败" + e.getMessage()); + } + } + + + /** + * 构造返回 + * @param fileName + * @return + */ + private UploadRespDTO generateResult(String fileName) { + + //获取加速域名 + String domain = conf.getUrl(); + + // 返回结果 + return new UploadRespDTO(domain + fileName); + } + + + /** + * 获取真实物理文件地址 + * @param uri + * @return + */ + public String getRealPath(String uri){ + + String regx = Constant.FILE_PREFIX+"(.*)"; + + // 查找全部变量 + Pattern pattern = Pattern.compile(regx); + Matcher m = pattern.matcher(uri); + if (m.find()) { + String str = m.group(1); + return conf.getDir() + str; + } + + return null; + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/upload/utils/FileUtils.java b/exam-api/src/main/java/com/yf/exam/ability/upload/utils/FileUtils.java new file mode 100644 index 0000000..539ecb0 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/upload/utils/FileUtils.java @@ -0,0 +1,172 @@ +package com.yf.exam.ability.upload.utils; + +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.yf.exam.core.utils.DateUtils; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Date; + +/** + * 文件工具类 + * @author bool + */ +public class FileUtils { + + /** + * 后缀分割符号 + */ + private static final String SUFFIX_SPLIT = "."; + + + /** + * 支持以断点的方式输出文件,提供文件在线预览和视频在线播放 + * @param request + * @param response + * @param filePath + * @throws IOException + */ + public static void writeRange(HttpServletRequest request, + HttpServletResponse response, String filePath) throws IOException { + + // 读取文件 + File file = new File(filePath); + + //只读模式 + RandomAccessFile randomFile = new RandomAccessFile(file, "r"); + long contentLength = randomFile.length(); + String range = request.getHeader("Range"); + int start = 0, end = 0; + if (range != null && range.startsWith("bytes=")) { + String[] values = range.split("=")[1].split("-"); + start = Integer.parseInt(values[0]); + if (values.length > 1) { + end = Integer.parseInt(values[1]); + } + } + int requestSize; + if (end != 0 && end > start) { + requestSize = end - start + 1; + } else { + requestSize = Integer.MAX_VALUE; + } + + byte[] buffer = new byte[128]; + response.setContentType(MediaUtils.getContentType(filePath)); + response.setHeader("Accept-Ranges", "bytes"); + response.setHeader("ETag", file.getName()); + response.setHeader("Last-Modified", new Date().toString()); + //第一次请求只返回content length来让客户端请求多次实际数据 + if (range == null) { + response.setHeader("Content-length", contentLength + ""); + } else { + //以后的多次以断点续传的方式来返回视频数据 + response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); + long requestStart = 0, requestEnd = 0; + String[] ranges = range.split("="); + if (ranges.length > 1) { + String[] rangeData = ranges[1].split("-"); + requestStart = Integer.parseInt(rangeData[0]); + if (rangeData.length > 1) { + requestEnd = Integer.parseInt(rangeData[1]); + } + } + long length; + if (requestEnd > 0) { + length = requestEnd - requestStart + 1; + response.setHeader("Content-length", "" + length); + response.setHeader("Content-Range", "bytes " + requestStart + "-" + requestEnd + "/" + contentLength); + } else { + length = contentLength - requestStart; + response.setHeader("Content-length", "" + length); + response.setHeader("Content-Range", "bytes " + requestStart + "-" + (contentLength - 1) + "/" + contentLength); + } + } + ServletOutputStream out = response.getOutputStream(); + int needSize = requestSize; + randomFile.seek(start); + while (needSize > 0) { + int len = randomFile.read(buffer); + if (needSize < buffer.length) { + out.write(buffer, 0, needSize); + } else { + out.write(buffer, 0, len); + if (len < buffer.length) { + break; + } + } + needSize -= buffer.length; + } + randomFile.close(); + out.close(); + } + + + + + /** + * 重命名文件 + * @param fileName + * @return + */ + public static String renameFile(String fileName) { + + //没有后缀名不处理 + if (!fileName.contains(SUFFIX_SPLIT)) { + return fileName; + } + + //文件后缀 + String extension = FilenameUtils.getExtension(fileName); + + //以系统时间命名 + return IdWorker.getIdStr() + "."+ extension; + + } + + + /** + * 处理新的文件路径,为上传文件预设目录,如:2021/01/01/xxx.jpg,要注意的是,前面没有斜杠 + * @param file 文件 + * @return + */ + public static String processPath(MultipartFile file){ + + // 创建OSSClient实例。 + String fileName = file.getOriginalFilename(); + + // 需要重命名 + fileName = renameFile(fileName); + + //获得上传的文件夹 + String dir = DateUtils.formatDate(new Date(), "yyyy/MM/dd/"); + + return new StringBuffer(dir).append(fileName).toString(); + + } + + /** + * 检查文件夹是否存在,不存在则创建 + * @param fileName + * @return + */ + public static void checkDir(String fileName){ + int index = fileName.lastIndexOf("/"); + if(index == -1){ + return; + } + + File file = new File(fileName.substring(0,index)); + if(!file.exists()){ + file.mkdirs(); + } + } + + +} diff --git a/exam-api/src/main/java/com/yf/exam/ability/upload/utils/MediaUtils.java b/exam-api/src/main/java/com/yf/exam/ability/upload/utils/MediaUtils.java new file mode 100644 index 0000000..b4394c0 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/ability/upload/utils/MediaUtils.java @@ -0,0 +1,47 @@ +package com.yf.exam.ability.upload.utils; + +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * 媒体工具,判断媒体类型 + * @author bool + * @date 2019-11-14 16:21 + */ +public class MediaUtils { + + public static final Map MEDIA_MAP = new HashMap(){ + { + + //本来是pdf的 + put(".pdf", "application/pdf"); + + //视频 + put(".mp4", "video,video/mp4"); + + } + }; + + /** + * 获得文件类型 + * @param filePath + * @return + */ + public static String getContentType(String filePath){ + + if(!StringUtils.isBlank(filePath) + && filePath.indexOf(".")!=-1) { + + // 后缀转换成小写 + String suffix = filePath.substring(filePath.lastIndexOf(".")).toLowerCase(); + + if (MEDIA_MAP.containsKey(suffix)) { + return MEDIA_MAP.get(suffix); + } + } + + return "application/octet-stream"; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/aspect/DictAspect.java b/exam-api/src/main/java/com/yf/exam/aspect/DictAspect.java new file mode 100644 index 0000000..441a154 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/aspect/DictAspect.java @@ -0,0 +1,317 @@ +package com.yf.exam.aspect; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.yf.exam.core.annon.Dict; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.utils.Reflections; +import com.yf.exam.modules.sys.system.service.SysDictService; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 数据字典AOP类,处理数据字典值 + * + * @author bool + */ +@Aspect +@Component +@Slf4j +public class DictAspect { + + @Autowired + private SysDictService sysDictService; + + /** + * 切入Controller执行 + * @param pjp + * @return + * @throws Throwable + */ + @Around("execution(public * com.yf.exam..*.*Controller.*(..))") + public Object doAround(ProceedingJoinPoint pjp) throws Throwable { + return this.translate(pjp); + } + + /** + * 进行翻译并返回,调用前必须实现:BaseDictService + * + * @param pjp + * @return + * @throws Throwable + */ + public Object translate(ProceedingJoinPoint pjp) throws Throwable { + // 处理字典 + return this.parseAllDictText(pjp.proceed()); + } + + /** + * 转换全部数据字典 + * + * @param result + */ + private Object parseAllDictText(Object result) { + + // 非ApiRest类型不处理 + if (result instanceof ApiRest) { + parseFullDictText(result); + } + + return result; + } + + + /** + * 转换所有类型的数据字典、包含子列表 + * + * @param result + */ + private void parseFullDictText(Object result) { + + try { + + Object rest = ((ApiRest) result).getData(); + + // 不处理普通数据类型 + if (rest == null || this.isBaseType(rest.getClass())) { + return; + } + + // 分页的 + if (rest instanceof IPage) { + List items = new ArrayList<>(16); + for (Object record : ((IPage) rest).getRecords()) { + Object item = this.parseObject(record); + items.add(item); + } + ((IPage) rest).setRecords(items); + return; + } + + // 数据列表的 + if (rest instanceof List) { + List items = new ArrayList<>(); + for (Object record : ((List) rest)) { + Object item = this.parseObject(record); + items.add(item); + } + // 重新回写值 + ((ApiRest) result).setData(items); + return; + } + + // 处理单对象 + Object item = this.parseObject(((ApiRest) result).getData()); + ((ApiRest) result).setData(item); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 处理数据字典值 + * + * @param record + * @return + */ + public Object parseObject(Object record) { + + if (record == null) { + return null; + } + + // 不处理普通数据类型 + if (this.isBaseType(record.getClass())) { + return record; + } + + // 转换JSON字符 + String json = JSON.toJSONString(record); + JSONObject item = JSONObject.parseObject(json); + + for (Field field : Reflections.getAllFields(record)) { + + // 如果是List类型 + if (List.class.isAssignableFrom(field.getType())) { + try { + List list = this.processList(field, item.getObject(field.getName(), List.class)); + item.put(field.getName(), list); + continue; + } catch (Exception e) { + e.printStackTrace(); + } + continue; + } + + // 处理普通字段 + if (field.getAnnotation(Dict.class) != null) { + String code = field.getAnnotation(Dict.class).dicCode(); + String text = field.getAnnotation(Dict.class).dicText(); + String table = field.getAnnotation(Dict.class).dictTable(); + String key = String.valueOf(item.get(field.getName())); + + //翻译字典值对应的txt + String textValue = this.translateDictValue(code, text, table, key); + if (StringUtils.isEmpty(textValue)) { + textValue = ""; + } + item.put(field.getName() + "_dictText", textValue); + continue; + } + + //日期格式转换 + if ("java.util.Date".equals(field.getType().getName()) && item.get(field.getName()) != null) { + + // 获取注解 + JsonFormat ann = field.getAnnotation(JsonFormat.class); + // 格式化方式 + SimpleDateFormat fmt; + + // 使用注解指定的 + if (ann != null && !StringUtils.isEmpty(ann.pattern())) { + fmt = new SimpleDateFormat(ann.pattern()); + } else { + // 默认时间样式 + fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + item.put(field.getName(), fmt.format(new Date((Long) item.get(field.getName())))); + continue; + + } + } + + return item; + } + + /** + * 获得类型为List的值 + * + * @param field + * @return + */ + private List processList(Field field, List list) { + + // 空判断 + if (list == null || list.size() == 0) { + return new ArrayList<>(); + } + + // 获得List属性的真实类 + Type genericType = field.getGenericType(); + Class actualType = null; + if (genericType instanceof ParameterizedType) { + // 尝试获取数据类型 + ParameterizedType pt = (ParameterizedType) genericType; + try { + actualType = (Class) pt.getActualTypeArguments()[0]; + }catch (Exception e){ + return list; + } + } + + // 常规列表无需处理 + if (isBaseType(actualType)) { + return list; + } + + // 返回列表 + List result = new ArrayList<>(16); + + for (int i = 0; i < list.size(); i++) { + // 创建实例-->赋值-->字典处理 + Object data = list.get(i); + try { + data = JSON.parseObject(JSON.toJSONString(data), actualType); + }catch (Exception e){ + // 转换出错不处理 + } + + // 处理后的数据 + Object pds = this.parseObject(data); + result.add(pds); + } + + return result; + } + + /** + * 翻译实现 + * + * @param code + * @param text + * @param table + * @param key + * @return + */ + private String translateDictValue(String code, String text, String table, String key) { + if (StringUtils.isEmpty(key)) { + return null; + } + try { + // 翻译值 + String dictText = null; + if (!StringUtils.isEmpty(table)) { + dictText = sysDictService.findDict(table, text, code, key.trim()); + } + + if (!StringUtils.isEmpty(dictText)) { + return dictText; + } + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + /** + * 判断是否基本类型 + * + * @param clazz + * @return + */ + private boolean isBaseType(Class clazz) { + + + // 基础数据类型 + if (clazz.equals(java.lang.Integer.class) || + clazz.equals(java.lang.Byte.class) || + clazz.equals(java.lang.Long.class) || + clazz.equals(java.lang.Double.class) || + clazz.equals(java.lang.Float.class) || + clazz.equals(java.lang.Character.class) || + clazz.equals(java.lang.Short.class) || + clazz.equals(java.lang.Boolean.class)) { + return true; + } + + // String类型 + if (clazz.equals(java.lang.String.class)) { + return true; + } + + // 数字 + if (clazz.equals(java.lang.Number.class)) { + return true; + } + + return false; + } + + +} diff --git a/exam-api/src/main/java/com/yf/exam/aspect/mybatis/QueryInterceptor.java b/exam-api/src/main/java/com/yf/exam/aspect/mybatis/QueryInterceptor.java new file mode 100644 index 0000000..6b958ca --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/aspect/mybatis/QueryInterceptor.java @@ -0,0 +1,144 @@ +package com.yf.exam.aspect.mybatis; + +import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; +import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO; +import lombok.extern.log4j.Log4j2; +import net.sf.jsqlparser.parser.CCJSqlParserManager; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Plugin; +import org.apache.ibatis.plugin.Signature; +import org.apache.ibatis.reflection.DefaultReflectorFactory; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.SystemMetaObject; +import org.apache.shiro.SecurityUtils; + +import java.io.StringReader; +import java.sql.Connection; +import java.util.Properties; + +/** + * 查询拦截器,用于拦截处理通用的信息、如用户ID、多租户信息等; + * 特别注意:此处继承了PaginationInterceptor分页,分页必须在拦截数据后执行,否则容易出现分页不准确,分页计数大于实际数量等问题 + * @author bool + */ +@Log4j2 +@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),}) +public class QueryInterceptor extends PaginationInterceptor implements Interceptor { + + /** + * 客户ID + */ + private static final String USER_FILTER = "{{userId}}"; + + + + @Override + public Object intercept(Invocation invocation) throws Throwable { + + StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); + MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory()); + MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); + + //sql语句类型 + SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); + + // 只过滤查询的 + if (SqlCommandType.SELECT == sqlCommandType) { + // 获得原始SQL + String sql = statementHandler.getBoundSql().getSql(); + + // 不处理 + if(!sql.contains(USER_FILTER)){ + return super.intercept(invocation); + } + // 处理SQL语句 + String outSql = this.parseSql(sql); + // 设置SQL + metaObject.setValue("delegate.boundSql.sql", outSql); + // 再分页 + return super.intercept(invocation); + } + + return invocation.proceed(); + } + + @Override + public Object plugin(Object target) { + return Plugin.wrap(target, this); + } + + @Override + public void setProperties(Properties properties) { + + } + + + + /** + * 获取当前登录用户 + * @return + */ + private SysUserLoginDTO getLoginUser() { + + try { + return SecurityUtils.getSubject().getPrincipal() != null ? (SysUserLoginDTO) SecurityUtils.getSubject().getPrincipal() : null; + } catch (Exception e) { + return null; + } + } + + /** + * 替换用户ID + * @param sql + * @return + */ + private String processUserId(String sql) { + + // 当前用户 + SysUserLoginDTO user = this.getLoginUser(); + String userId = user.getId(); + if(StringUtils.isNotBlank(userId)){ + return sql.replace(USER_FILTER, userId); + } + return null; + } + + /** + * 处理注入用户信息 + * @param src + * @return + */ + private String parseSql(String src) { + + CCJSqlParserManager parserManager = new CCJSqlParserManager(); + try { + + Select select = (Select) parserManager.parse(new StringReader(src)); + PlainSelect selectBody = (PlainSelect) select.getSelectBody(); + + // 过滤客户 + String sql = selectBody.toString(); + + // 过滤用户ID + sql = this.processUserId(sql); + + // 获得SQL + return sql; + + } catch (Exception e) { + e.printStackTrace(); + } + + return src; + } + + +} diff --git a/exam-api/src/main/java/com/yf/exam/aspect/mybatis/UpdateInterceptor.java b/exam-api/src/main/java/com/yf/exam/aspect/mybatis/UpdateInterceptor.java new file mode 100644 index 0000000..8baae8f --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/aspect/mybatis/UpdateInterceptor.java @@ -0,0 +1,80 @@ +package com.yf.exam.aspect.mybatis; + +import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Plugin; +import org.apache.ibatis.plugin.Signature; + +import java.lang.reflect.Field; +import java.sql.Timestamp; +import java.util.Objects; +import java.util.Properties; + +/** + * 自动给创建时间个更新时间加值 + * @author bool + */ +@Intercepts(value = {@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) +public class UpdateInterceptor extends AbstractSqlParserHandler implements Interceptor { + + /** + * 创建时间 + */ + private static final String CREATE_TIME = "createTime"; + /** + * 更新时间 + */ + private static final String UPDATE_TIME = "updateTime"; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; + // SQL操作命令 + SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); + // 获取新增或修改的对象参数 + Object parameter = invocation.getArgs()[1]; + // 获取对象中所有的私有成员变量(对应表字段) + Field[] declaredFields = parameter.getClass().getDeclaredFields(); + if (parameter.getClass().getSuperclass() != null) { + Field[] superField = parameter.getClass().getSuperclass().getDeclaredFields(); + declaredFields = ArrayUtils.addAll(declaredFields, superField); + } + + String fieldName = null; + for (Field field : declaredFields) { + fieldName = field.getName(); + if (Objects.equals(CREATE_TIME, fieldName)) { + if (SqlCommandType.INSERT.equals(sqlCommandType)) { + field.setAccessible(true); + field.set(parameter, new Timestamp(System.currentTimeMillis())); + } + } + if (Objects.equals(UPDATE_TIME, fieldName)) { + if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) { + field.setAccessible(true); + field.set(parameter, new Timestamp(System.currentTimeMillis())); + + } + } + } + return invocation.proceed(); + } + + @Override + public Object plugin(Object target) { + if (target instanceof Executor) { + return Plugin.wrap(target, this); + } + return target; + } + + @Override + public void setProperties(Properties properties) { + } +} diff --git a/exam-api/src/main/java/com/yf/exam/aspect/utils/InjectUtils.java b/exam-api/src/main/java/com/yf/exam/aspect/utils/InjectUtils.java new file mode 100644 index 0000000..4b47fee --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/aspect/utils/InjectUtils.java @@ -0,0 +1,99 @@ +package com.yf.exam.aspect.utils; + +import com.alibaba.fastjson.JSON; +import com.yf.exam.core.api.ApiError; +import com.yf.exam.core.api.ApiRest; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.reflect.Field; + +/** + * 注入工具类 + * @author bool + * @date 2019-07-17 09:32 + */ +@Log4j2 +@Component +public class InjectUtils { + + + + /** + * 给对象字段赋值 + * + * @param object 赋值的对象 + * @param value 值 + * @param fields 字段 + * @throws Exception 异常 + */ + public void setValue(Object object, Object value, String... fields) throws Exception { + + //设置同类的属性 + for (String fieldName : fields) { + + //获取当前 + Field field = this.getFiled(object.getClass(), fieldName); + if(field == null){ + continue; + } + + field.setAccessible(true); + field.set(object, value); + } + + } + + /** + * 获取字段名对应的字段 + * + * @param clazz 目标类 + * @param fieldName 字段名 + */ + private Field getFiled(Class clazz, String fieldName) { + + System.out.println("注入的类:"+clazz.toString()); + + //是否具有包含关系 + try { + //获取当前类的属性 + return clazz.getDeclaredField(fieldName); + }catch (Exception e){ + + log.error(clazz.toString() + ": not exist field, try superclass " + fieldName); + + //如果为空且存在父类,则往上找 + if(clazz.getSuperclass()!=null){ + return this.getFiled(clazz.getSuperclass(), fieldName); + } + + return null; + } + } + + + /** + * 打印结果返回 + * @param response + * @throws IOException + */ + public static void restError(HttpServletResponse response) { + + try { + + //固定错误 + ApiRest apiRest = new ApiRest(ApiError.ERROR_10010002); + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/json"); + response.getWriter().write(JSON.toJSONString(apiRest)); + response.getWriter().close(); + + }catch (IOException e){ + + } + + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/config/CorsConfig.java b/exam-api/src/main/java/com/yf/exam/config/CorsConfig.java new file mode 100644 index 0000000..e88cb08 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/config/CorsConfig.java @@ -0,0 +1,35 @@ +package com.yf.exam.config; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + + +/** + * 网关全局设置,允许跨域 + * @author bool + * @date 2019-08-13 17:28 + */ + +@Configuration +public class CorsConfig { + + @Bean + public FilterRegistrationBean corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOrigin(CorsConfiguration.ALL); + config.addAllowedHeader(CorsConfiguration.ALL); + config.addAllowedMethod(CorsConfiguration.ALL); + source.registerCorsConfiguration("/**", config); + FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); + bean.setOrder(Ordered.HIGHEST_PRECEDENCE); + return bean; + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/config/MultipartConfig.java b/exam-api/src/main/java/com/yf/exam/config/MultipartConfig.java new file mode 100644 index 0000000..75e6dfa --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/config/MultipartConfig.java @@ -0,0 +1,28 @@ +package com.yf.exam.config; + +import org.springframework.boot.web.servlet.MultipartConfigFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.unit.DataSize; + +import javax.servlet.MultipartConfigElement; + +/** + * 文件上传配置 + * @author bool + * @date 2019-07-29 16:23 + */ +@Configuration +public class MultipartConfig { + + @Bean + public MultipartConfigElement multipartConfigElement() { + MultipartConfigFactory factory = new MultipartConfigFactory(); + // 单个数据大小 + factory.setMaxFileSize(DataSize.ofMegabytes(5000L)); + /// 总上传数据大小 + factory.setMaxRequestSize(DataSize.ofMegabytes(5000L)); + return factory.createMultipartConfig(); + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/config/MybatisConfig.java b/exam-api/src/main/java/com/yf/exam/config/MybatisConfig.java new file mode 100644 index 0000000..642fc25 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/config/MybatisConfig.java @@ -0,0 +1,37 @@ +package com.yf.exam.config; + +import com.yf.exam.aspect.mybatis.QueryInterceptor; +import com.yf.exam.aspect.mybatis.UpdateInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Mybatis过滤器配置 + * 注意:必须按顺序进行配置,否则容易出现业务异常 + * @author bool + */ +@Configuration +@MapperScan("com.yf.exam.modules.**.mapper") +public class MybatisConfig { + + /** + * 数据查询过滤器 + */ + @Bean + public QueryInterceptor queryInterceptor() { + QueryInterceptor query = new QueryInterceptor(); + query.setLimit(-1L); + return query; + } + + /** + * 插入数据过滤器 + */ + @Bean + public UpdateInterceptor updateInterceptor() { + return new UpdateInterceptor(); + } + + +} \ No newline at end of file diff --git a/exam-api/src/main/java/com/yf/exam/config/ScheduledConfig.java b/exam-api/src/main/java/com/yf/exam/config/ScheduledConfig.java new file mode 100644 index 0000000..b08f96f --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/config/ScheduledConfig.java @@ -0,0 +1,77 @@ +package com.yf.exam.config; + +import lombok.extern.log4j.Log4j2; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 任务调度配置 + * @author bool + */ +@Log4j2 +@Configuration +@EnableScheduling +@EnableAsync +public class ScheduledConfig implements SchedulingConfigurer, AsyncConfigurer { + + /** + * 定时任务使用的线程池 + * @return + */ + @Bean(destroyMethod = "shutdown", name = "taskScheduler") + public ThreadPoolTaskScheduler taskScheduler(){ + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(10); + scheduler.setThreadNamePrefix("task-"); + scheduler.setAwaitTerminationSeconds(600); + scheduler.setWaitForTasksToCompleteOnShutdown(true); + return scheduler; + } + + /** + * 异步任务执行线程池 + * @return + */ + @Bean(name = "asyncExecutor") + public ThreadPoolTaskExecutor asyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(10); + executor.setQueueCapacity(1000); + executor.setKeepAliveSeconds(600); + executor.setMaxPoolSize(20); + executor.setThreadNamePrefix("taskExecutor-"); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } + + @Override + public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { + ThreadPoolTaskScheduler taskScheduler = taskScheduler(); + scheduledTaskRegistrar.setTaskScheduler(taskScheduler); + } + + @Override + public Executor getAsyncExecutor() { + return asyncExecutor(); + } + + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return (throwable, method, objects) -> { + log.error("异步任务执行出现异常, message {}, emthod {}, params {}", throwable, method, objects); + }; + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/config/ShiroConfig.java b/exam-api/src/main/java/com/yf/exam/config/ShiroConfig.java new file mode 100644 index 0000000..cbb3f38 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/config/ShiroConfig.java @@ -0,0 +1,127 @@ +package com.yf.exam.config; + +import com.yf.exam.ability.shiro.CNFilterFactoryBean; +import com.yf.exam.ability.shiro.ShiroRealm; +import com.yf.exam.ability.shiro.aop.JwtFilter; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; +import org.apache.shiro.mgt.DefaultSubjectDAO; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.spring.LifecycleBeanPostProcessor; +import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; + +import javax.servlet.Filter; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + + +/** + * Shiro配置类 + * @author bool + */ +@Slf4j +@Configuration +public class ShiroConfig { + + /** + * Filter Chain定义说明 + * + * 1、一个URL可以配置多个Filter,使用逗号分隔 + * 2、当设置多个过滤器时,全部验证通过,才视为通过 + * 3、部分过滤器可指定参数,如perms,roles + */ + @Bean("shiroFilterFactoryBean") + public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { + ShiroFilterFactoryBean shiroFilterFactoryBean = new CNFilterFactoryBean(); + shiroFilterFactoryBean.setSecurityManager(securityManager); + // 拦截器 + Map map = new LinkedHashMap<>(); + + // 需要排除的一些接口 + map.put("/exam/api/sys/user/login", "anon"); + map.put("/exam/api/sys/user/reg", "anon"); + map.put("/exam/api/sys/user/quick-reg", "anon"); + + // 获取网站基本信息 + map.put("/exam/api/sys/config/detail", "anon"); + + // 文件读取 + map.put("/upload/file/**", "anon"); + + map.put("/", "anon"); + map.put("/v2/**", "anon"); + map.put("/doc.html", "anon"); + map.put("/**/*.js", "anon"); + map.put("/**/*.css", "anon"); + map.put("/**/*.html", "anon"); + map.put("/**/*.svg", "anon"); + map.put("/**/*.pdf", "anon"); + map.put("/**/*.jpg", "anon"); + map.put("/**/*.png", "anon"); + map.put("/**/*.ico", "anon"); + + // 字体 + map.put("/**/*.ttf", "anon"); + map.put("/**/*.woff", "anon"); + map.put("/**/*.woff2", "anon"); + map.put("/druid/**", "anon"); + map.put("/swagger-ui.html", "anon"); + map.put("/swagger**/**", "anon"); + map.put("/webjars/**", "anon"); + + // 添加自己的过滤器并且取名为jwt + Map filterMap = new HashMap(1); + filterMap.put("jwt", new JwtFilter()); + shiroFilterFactoryBean.setFilters(filterMap); + map.put("/**", "jwt"); + + shiroFilterFactoryBean.setFilterChainDefinitionMap(map); + return shiroFilterFactoryBean; + } + + @Bean("securityManager") + public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) { + DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); + securityManager.setRealm(myRealm); + DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); + DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); + defaultSessionStorageEvaluator.setSessionStorageEnabled(false); + subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); + securityManager.setSubjectDAO(subjectDAO); + return securityManager; + } + + /** + * 下面的代码是添加注解支持 + * @return + */ + @Bean + @DependsOn("lifecycleBeanPostProcessor") + public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { + DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); + defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); + defaultAdvisorAutoProxyCreator.setUsePrefix(true); + defaultAdvisorAutoProxyCreator.setAdvisorBeanNamePrefix("_no_advisor"); + return defaultAdvisorAutoProxyCreator; + } + + @Bean + public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { + return new LifecycleBeanPostProcessor(); + } + + @Bean + public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { + AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); + advisor.setSecurityManager(securityManager); + return advisor; + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/config/SwaggerConfig.java b/exam-api/src/main/java/com/yf/exam/config/SwaggerConfig.java new file mode 100644 index 0000000..d4208aa --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/config/SwaggerConfig.java @@ -0,0 +1,65 @@ +package com.yf.exam.config; + +import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI; +import io.swagger.annotations.ApiOperation; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.Contact; +import springfox.documentation.service.SecurityScheme; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.util.Collections; + +/** + * Swagger配置 + * @author bool + * @date 2020/8/19 20:53 + */ +@Configuration +@EnableSwagger2 +@EnableSwaggerBootstrapUI +@ConfigurationProperties(prefix = "swagger") +public class SwaggerConfig { + + + @Bean + public Docket examApi() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .groupName("考试模块接口") + .select() + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.ant("/exam/api/**")) + .build() + .securitySchemes(Collections.singletonList(securityScheme())); + } + + + + private ApiInfo apiInfo() { + return new ApiInfoBuilder().title("考试系统接口") + .description("考试系统接口") + .contact(new Contact("Van", "https://exam.yfhl.net", "18365918@qq.com")) + .version("1.0.0") + .build(); + } + + + /** + * 授权头部 + * @return + */ + @Bean + SecurityScheme securityScheme() { + return new ApiKey("token", "token", "header"); + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/annon/Dict.java b/exam-api/src/main/java/com/yf/exam/core/annon/Dict.java new file mode 100644 index 0000000..4aa3f07 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/annon/Dict.java @@ -0,0 +1,21 @@ +package com.yf.exam.core.annon; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据字典注解 + * @author bool + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Dict { + + String dicCode(); + + String dicText() default ""; + + String dictTable() default ""; +} diff --git a/exam-api/src/main/java/com/yf/exam/core/api/ApiError.java b/exam-api/src/main/java/com/yf/exam/core/api/ApiError.java new file mode 100644 index 0000000..f299fac --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/api/ApiError.java @@ -0,0 +1,67 @@ +package com.yf.exam.core.api; + + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 全局错误码定义,用于定义接口的响应数据, + * 枚举名称全部使用代码命名,在系统中调用,免去取名难的问题。 + * @author bool + * @date 2019-06-14 21:15 + */ +@NoArgsConstructor +@AllArgsConstructor +public enum ApiError implements Serializable { + + + /** + * 通用错误,接口参数不全 + */ + ERROR_10010001("参数不全或类型错误!"), + ERROR_10010002("您还未登录,请先登录!"), + ERROR_10010003("数据不存在!"), + ERROR_10010012("图形验证码错误!"), + ERROR_10010013("短信验证码错误!"), + ERROR_10010014("不允许重复评论!"), + + /** + * 考试相关错误 + */ + ERROR_20010001("试题被删除,无法继续考试!"), + ERROR_20010002("您有正在进行的考试!"), + + + ERROR_90010001("账号不存在,请确认!"), + ERROR_90010002("账号或密码错误!"), + ERROR_90010003("至少要包含一个角色!"), + ERROR_90010004("管理员账号无法修改!"), + ERROR_90010005("账号被禁用,请联系管理员!"), + ERROR_90010006("活动用户不足,无法开启竞拍!"), + ERROR_90010007("旧密码不正确,请确认!"), + + + ERROR_60000001("数据不存在!"); + + public String msg; + + /** + * 生成Markdown格式文档,用于更新文档用的 + * @param args + */ + public static void main(String[] args) { + for (ApiError e : ApiError.values()) { + System.out.println("'"+e.name().replace("ERROR_", "")+"':'"+e.msg+"',"); + } + } + + /** + * 获取错误码 + * @return + */ + public Integer getCode(){ + return Integer.parseInt(this.name().replace("ERROR_", "")); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/core/api/ApiRest.java b/exam-api/src/main/java/com/yf/exam/core/api/ApiRest.java new file mode 100644 index 0000000..4785bfb --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/api/ApiRest.java @@ -0,0 +1,64 @@ +package com.yf.exam.core.api; + + +import com.yf.exam.core.api.ApiError; +import com.yf.exam.core.exception.ServiceException; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 数据结果返回的封装 + * @author bool + * @date 2018/11/20 09:48 + */ +@Data +@NoArgsConstructor +@ApiModel(value="接口响应", description="接口响应") +public class ApiRest{ + + /** + * 响应消息 + */ + @ApiModelProperty(value = "响应消息") + private String msg; + /** + * 响应代码 + */ + @ApiModelProperty(value = "响应代码,0为成功,1为失败", required = true) + private Integer code; + + /** + * 请求或响应body + */ + @ApiModelProperty(value = "响应内容") + protected T data; + + + /** + * 是否成功 + * @return + */ + public boolean isSuccess(){ + return code.equals(0); + } + + /** + * 构造函数 + * @param error + */ + public ApiRest(ServiceException error){ + this.code = error.getCode(); + this.msg = error.getMsg(); + } + + /** + * 构造函数 + * @param error + */ + public ApiRest(ApiError error){ + this.code = error.getCode(); + this.msg = error.msg; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/core/api/controller/BaseController.java b/exam-api/src/main/java/com/yf/exam/core/api/controller/BaseController.java new file mode 100644 index 0000000..22fcf09 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/api/controller/BaseController.java @@ -0,0 +1,154 @@ +package com.yf.exam.core.api.controller; + + +import com.yf.exam.core.api.ApiError; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.exception.ServiceException; + +/** + * 基础控制器 + * @author Dav + */ +public class BaseController { + + /** + * 成功默认消息 + */ + private static final Integer CODE_SUCCESS = 0; + private static final String MSG_SUCCESS = "操作成功!"; + + /** + * 失败默认消息 + */ + private static final Integer CODE_FAILURE = 1; + private static final String MSG_FAILURE = "请求失败!"; + + + /** + * 完成消息构造 + * @param code + * @param message + * @param data + * @param + * @return + */ + protected ApiRest message(Integer code, String message, T data){ + ApiRest response = new ApiRest<>(); + response.setCode(code); + response.setMsg(message); + if(data!=null) { + response.setData(data); + } + return response; + } + + /** + * 请求成功空数据 + * @param + * @return + */ + protected ApiRest success(){ + return message(0, "请求成功!", null); + } + + + + /** + * 请求成功,通用代码 + * @param message + * @param data + * @param + * @return + */ + protected ApiRest success(String message, T data){ + return message(CODE_SUCCESS, message, data); + } + + + /** + * 请求成功,仅内容 + * @param data + * @param + * @return + */ + protected ApiRest success(T data){ + return message(CODE_SUCCESS, MSG_SUCCESS, data); + } + + + /** + * 请求失败,完整构造 + * @param code + * @param message + * @param data + * @param + * @return + */ + protected ApiRest failure(Integer code, String message, T data){ + return message(code, message, data); + } + + /** + * 请求失败,消息和内容 + * @param message + * @param data + * @param + * @return + */ + protected ApiRest failure(String message, T data){ + return message(CODE_FAILURE, message, data); + } + + /** + * 请求失败,消息 + * @param message + * @return + */ + protected ApiRest failure(String message){ + return message(CODE_FAILURE, message, null); + } + + /** + * 请求失败,仅内容 + * @param data + * @param + * @return + */ + protected ApiRest failure(T data){ + return message(CODE_FAILURE, MSG_FAILURE, data); + } + + + /** + * 请求失败,仅内容 + * @param + * @return + */ + protected ApiRest failure(){ + return message(CODE_FAILURE, MSG_FAILURE, null); + } + + + + /** + * 请求失败,仅内容 + * @param + * @return + */ + protected ApiRest failure(ApiError error, T data){ + return message(error.getCode(), error.msg, data); + } + + + + /** + * 请求失败,仅内容 + * @param ex + * @param + * @return + */ + protected ApiRest failure(ServiceException ex){ + ApiRest apiRest = message(ex.getCode(), ex.getMsg(), null); + return apiRest; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseDTO.java b/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseDTO.java new file mode 100644 index 0000000..a2f6a3e --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseDTO.java @@ -0,0 +1,15 @@ +package com.yf.exam.core.api.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 请求和响应的基础类,用于处理序列化 + * @author dav + * @date 2019/3/16 15:56 + */ +@Data +public class BaseDTO implements Serializable { + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseIdReqDTO.java b/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseIdReqDTO.java new file mode 100644 index 0000000..074fea3 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseIdReqDTO.java @@ -0,0 +1,28 @@ +package com.yf.exam.core.api.dto; + +import com.yf.exam.core.api.dto.BaseDTO; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *

+ * 主键通用请求类,用于根据ID查询 + *

+ * + * @author 聪明笨狗 + * @since 2019-04-20 12:15 + */ +@Data +@ApiModel(value="主键通用请求类", description="主键通用请求类") +public class BaseIdReqDTO extends BaseDTO { + + + @ApiModelProperty(value = "主键ID", required=true) + private String id; + + @JsonIgnore + private String userId; + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseIdRespDTO.java b/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseIdRespDTO.java new file mode 100644 index 0000000..a9a59e2 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseIdRespDTO.java @@ -0,0 +1,26 @@ +package com.yf.exam.core.api.dto; + +import com.yf.exam.core.api.dto.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 主键通用响应类,用于添加后返回内容 + *

+ * + * @author 聪明笨狗 + * @since 2019-04-20 12:15 + */ +@Data +@ApiModel(value="主键通用响应类", description="主键通用响应类") +@AllArgsConstructor +@NoArgsConstructor +public class BaseIdRespDTO extends BaseDTO { + + @ApiModelProperty(value = "主键ID", required=true) + private String id; +} diff --git a/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseIdsReqDTO.java b/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseIdsReqDTO.java new file mode 100644 index 0000000..df80c18 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseIdsReqDTO.java @@ -0,0 +1,26 @@ +package com.yf.exam.core.api.dto; + +import com.yf.exam.core.api.dto.BaseDTO; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 通用ID列表类操作,用于批量删除、修改状态等 + * @author bool + * @date 2019-08-01 19:07 + */ +@Data +@ApiModel(value="删除参数", description="删除参数") +public class BaseIdsReqDTO extends BaseDTO { + + + @JsonIgnore + private String userId; + + @ApiModelProperty(value = "要删除的ID列表", required = true) + private List ids; +} diff --git a/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseStateReqDTO.java b/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseStateReqDTO.java new file mode 100644 index 0000000..15f26cb --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/api/dto/BaseStateReqDTO.java @@ -0,0 +1,32 @@ +package com.yf.exam.core.api.dto; + +import com.yf.exam.core.api.dto.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + *

+ * 通用状态请求类,用于修改状态什么的 + *

+ * + * @author 聪明笨狗 + * @since 2019-04-20 12:15 + */ +@Data +@ApiModel(value="通用状态请求类", description="通用状态请求类") +@AllArgsConstructor +@NoArgsConstructor +public class BaseStateReqDTO extends BaseDTO { + + + @ApiModelProperty(value = "要修改对象的ID列表", required=true) + private List ids; + + @ApiModelProperty(value = "通用状态,0为正常,1为禁用", required=true) + private Integer state; +} diff --git a/exam-api/src/main/java/com/yf/exam/core/api/dto/PagingReqDTO.java b/exam-api/src/main/java/com/yf/exam/core/api/dto/PagingReqDTO.java new file mode 100644 index 0000000..276a0e0 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/api/dto/PagingReqDTO.java @@ -0,0 +1,47 @@ +package com.yf.exam.core.api.dto; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 分页查询类 + * @param + * @author bool + */ +@ApiModel(value="分页参数", description="分页参数") +@Data +public class PagingReqDTO { + + + @ApiModelProperty(value = "当前页码", required = true, example = "1") + private Integer current; + + @ApiModelProperty(value = "每页数量", required = true, example = "10") + private Integer size; + + @ApiModelProperty(value = "查询参数") + private T params; + + @ApiModelProperty(value = "排序字符") + private String orderBy; + + @JsonIgnore + @ApiModelProperty(value = "当前用户的ID") + private String userId; + + /** + * 转换成MyBatis的简单分页对象 + * @return + */ + public Page toPage(){ + Page page = new Page(); + page.setCurrent(this.current); + page.setSize(this.size); + return page; + } + + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/api/dto/PagingRespDTO.java b/exam-api/src/main/java/com/yf/exam/core/api/dto/PagingRespDTO.java new file mode 100644 index 0000000..3b2a8f7 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/api/dto/PagingRespDTO.java @@ -0,0 +1,30 @@ +package com.yf.exam.core.api.dto; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +/** + * 分页响应类 + * @author bool + * @date 2019-07-20 15:17 + * @param + */ +public class PagingRespDTO extends Page { + + /** + * 获取页面总数量 + * @return + */ + @Override + public long getPages() { + if (this.getSize() == 0L) { + return 0L; + } else { + long pages = this.getTotal() / this.getSize(); + if (this.getTotal() % this.getSize() != 0L) { + ++pages; + } + return pages; + } + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/api/utils/JsonConverter.java b/exam-api/src/main/java/com/yf/exam/core/api/utils/JsonConverter.java new file mode 100644 index 0000000..f9e4622 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/api/utils/JsonConverter.java @@ -0,0 +1,48 @@ +package com.yf.exam.core.api.utils; + +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * JSON数据转换器,用于转换返回消息的格式 + * @author dav + * @date 2018/9/11 19:30 + */ +public class JsonConverter { + + /** + * FastJson消息转换器 + * + * @return + */ + public static HttpMessageConverter fastConverter() { + // 定义一个convert转换消息的对象 + FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); + // 添加FastJson的配置信息 + FastJsonConfig fastJsonConfig = new FastJsonConfig(); + // 默认转换器 + fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat, + SerializerFeature.WriteNullNumberAsZero, + SerializerFeature.MapSortField, + SerializerFeature.WriteNullStringAsEmpty, + SerializerFeature.DisableCircularReferenceDetect, + SerializerFeature.WriteDateUseDateFormat, + SerializerFeature.WriteNullListAsEmpty); + fastJsonConfig.setCharset(Charset.forName("UTF-8")); + // 处理中文乱码问题 + List fastMediaTypes = new ArrayList<>(); + fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); + fastConverter.setSupportedMediaTypes(fastMediaTypes); + // 在convert中添加配置信息 + fastConverter.setFastJsonConfig(fastJsonConfig); + + return fastConverter; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/core/enums/CommonState.java b/exam-api/src/main/java/com/yf/exam/core/enums/CommonState.java new file mode 100644 index 0000000..21ef637 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/enums/CommonState.java @@ -0,0 +1,19 @@ +package com.yf.exam.core.enums; + +/** + * 通用的状态枚举信息 + * + * @author bool + * @date 2019-09-17 17:57 + */ +public interface CommonState { + + /** + * 普通状态,正常的 + */ + Integer NORMAL = 0; + /** + * 非正常状态,禁用,下架等 + */ + Integer ABNORMAL = 1; +} diff --git a/exam-api/src/main/java/com/yf/exam/core/enums/OpenType.java b/exam-api/src/main/java/com/yf/exam/core/enums/OpenType.java new file mode 100644 index 0000000..0012db5 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/enums/OpenType.java @@ -0,0 +1,18 @@ +package com.yf.exam.core.enums; + +/** + * 开放方式 + * @author bool + */ +public interface OpenType { + + /** + * 完全开放 + */ + Integer OPEN = 1; + + /** + * 部门开放 + */ + Integer DEPT_OPEN = 2; +} diff --git a/exam-api/src/main/java/com/yf/exam/core/exception/ServiceException.java b/exam-api/src/main/java/com/yf/exam/core/exception/ServiceException.java new file mode 100644 index 0000000..2d474c1 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/exception/ServiceException.java @@ -0,0 +1,51 @@ +package com.yf.exam.core.exception; + +import com.yf.exam.core.api.ApiError; +import com.yf.exam.core.api.ApiRest; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ServiceException extends RuntimeException{ + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误消息 + */ + private String msg; + + /** + * 从结果初始化 + * @param apiRest + */ + public ServiceException(ApiRest apiRest){ + this.code = apiRest.getCode(); + this.msg = apiRest.getMsg(); + } + + /** + * 从枚举中获取参数 + * @param apiError + */ + public ServiceException(ApiError apiError){ + this.code = apiError.getCode(); + this.msg = apiError.msg; + } + + /** + * 异常构造 + * @param msg + */ + public ServiceException(String msg){ + this.code = 1; + this.msg = msg; + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/exception/ServiceExceptionHandler.java b/exam-api/src/main/java/com/yf/exam/core/exception/ServiceExceptionHandler.java new file mode 100644 index 0000000..1c8de5d --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/exception/ServiceExceptionHandler.java @@ -0,0 +1,46 @@ +package com.yf.exam.core.exception; + +import com.yf.exam.core.api.ApiRest; +import org.springframework.http.HttpStatus; +import org.springframework.ui.Model; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.*; + +/** + * 统一异常处理类 + * @author bool + * @date 2019-06-21 19:27 + */ +@RestControllerAdvice +public class ServiceExceptionHandler { + + /** + * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器 + * @param binder + */ + @InitBinder + public void initWebBinder(WebDataBinder binder){ + + } + + /** + * 把值绑定到Model中,使全局@RequestMapping可以获取到该值 + * @param model + */ + @ModelAttribute + public void addAttribute(Model model) { + + } + + /** + * 捕获ServiceException + * @param e + * @return + */ + @ExceptionHandler({com.yf.exam.core.exception.ServiceException.class}) + @ResponseStatus(HttpStatus.OK) + public ApiRest serviceExceptionHandler(ServiceException e) { + return new ApiRest(e); + } + +} \ No newline at end of file diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/BeanMapper.java b/exam-api/src/main/java/com/yf/exam/core/utils/BeanMapper.java new file mode 100644 index 0000000..8ee5ca4 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/BeanMapper.java @@ -0,0 +1,59 @@ +package com.yf.exam.core.utils; + +import org.dozer.DozerBeanMapper; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + + +/** + * 简单封装Dozer, 实现深度转换Bean<->Bean的Mapper.实现: + * + * 1. 持有Mapper的单例. + * 2. 返回值类型转换. + * 3. 批量转换Collection中的所有对象. + * 4. 区分创建新的B对象与将对象A值复制到已存在的B对象两种函数. + * + */ +public class BeanMapper { + + /** + * 持有Dozer单例, 避免重复创建DozerMapper消耗资源. + */ + private static DozerBeanMapper dozerBeanMapper = new DozerBeanMapper(); + + /** + * 基于Dozer转换对象的类型. + */ + public static T map(Object source, Class destinationClass) { + return dozerBeanMapper.map(source, destinationClass); + } + + /** + * 基于Dozer转换Collection中对象的类型. + */ + public static List mapList(Iterable sourceList, Class destinationClass) { + List destinationList = new ArrayList(); + for (Object sourceObject : sourceList) { + T destinationObject = dozerBeanMapper.map(sourceObject, destinationClass); + destinationList.add(destinationObject); + } + return destinationList; + } + + /** + * 基于Dozer将对象A的值拷贝到对象B中. + */ + public static void copy(Object source, Object destinationObject) { + if(source!=null) { + dozerBeanMapper.map(source, destinationObject); + } + } + + public static List mapList(Collection source, Function mapper) { + return source.stream().map(mapper).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/CronUtils.java b/exam-api/src/main/java/com/yf/exam/core/utils/CronUtils.java new file mode 100644 index 0000000..c019b3e --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/CronUtils.java @@ -0,0 +1,31 @@ +package com.yf.exam.core.utils; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * 时间转换quartz表达式 + * @author bool + * @date 2020/11/29 下午3:00 + */ +public class CronUtils { + + /** + * 格式化数据 + */ + private static final String DATE_FORMAT = "ss mm HH dd MM ? yyyy"; + + /** + * 准确的时间点到表达式 + * @param date + * @return + */ + public static String dateToCron(final Date date){ + SimpleDateFormat fmt = new SimpleDateFormat(DATE_FORMAT); + String formatTimeStr = ""; + if (date != null) { + formatTimeStr = fmt.format(date); + } + return formatTimeStr; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/DateUtils.java b/exam-api/src/main/java/com/yf/exam/core/utils/DateUtils.java new file mode 100644 index 0000000..4121ef5 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/DateUtils.java @@ -0,0 +1,103 @@ +package com.yf.exam.core.utils; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +/** + * 日期处理工具类 + * ClassName: DateUtils
+ * date: 2018年12月13日 下午6:34:02
+ * + * @author Bool + * @version + */ +public class DateUtils { + + /** + * + * calcExpDays:计算某个日期与当前日期相差的天数,如果计算的日期大于现在时间,将返回负数;否则返回正数
+ * @author Bool + * @param userCreateTime + * @return + * @since JDK 1.6 + */ + public static int calcExpDays(Date userCreateTime){ + + Calendar start = Calendar.getInstance(); + start.setTime(userCreateTime); + + Calendar now = Calendar.getInstance(); + now.setTime(new Date()); + + long l = now.getTimeInMillis() - start.getTimeInMillis(); + int days = new Long(l / (1000 * 60 * 60 * 24)).intValue(); + return days; + } + + + /** + * + * dateNow:获取当前时间的字符串格式,根据传入的格式化来展示.
+ * @author Bool + * @param format 日期格式化 + * @return + */ + public static String dateNow(String format) { + SimpleDateFormat fmt = new SimpleDateFormat(format); + Calendar c = new GregorianCalendar(); + return fmt.format(c.getTime()); + } + + /** + * formatDate:格式化日期,返回指定的格式
+ * @author Bool + * @param time + * @param format + * @return + */ + public static String formatDate(Date time, String format) { + SimpleDateFormat fmt = new SimpleDateFormat(format); + return fmt.format(time.getTime()); + } + + + + /** + * parseDate:将字符串转换成日期,使用:yyyy-MM-dd HH:mm:ss 来格式化 + * @author Bool + * @param date + * @return + */ + public static Date parseDate(String date) { + return parseDate(date, "yyyy-MM-dd HH:mm:ss"); + } + + + /** + * + * parseDate:将字符串转换成日期,使用指定格式化来格式化 + * @author Bool + * @param date + * @param pattern + * @return + */ + public static Date parseDate(String date, String pattern) { + + if (pattern==null) { + pattern = "yyyy-MM-dd HH:mm:ss"; + } + + SimpleDateFormat fmt = new SimpleDateFormat(pattern); + + try { + + return fmt.parse(date); + } catch (Exception ex) { + ex.printStackTrace(); + } + return null; + + } +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/IpUtils.java b/exam-api/src/main/java/com/yf/exam/core/utils/IpUtils.java new file mode 100644 index 0000000..91277d7 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/IpUtils.java @@ -0,0 +1,65 @@ +package com.yf.exam.core.utils; + + +import javax.servlet.http.HttpServletRequest; + +/** + * IP获取工具类,用户获取网络请求过来的真实IP + * ClassName: IpUtils
+ * date: 2018年2月13日 下午7:27:52
+ * + * @author Bool + * @version + */ +public class IpUtils { + + + /** + * + * getClientIp:通过请求获取客户端的真实IP地址 + * @author Bool + * @param request + * @return + */ + public static String extractClientIp(HttpServletRequest request) { + + String ip = null; + + //X-Forwarded-For:Squid 服务代理 + String ipAddresses = request.getHeader("X-Forwarded-For"); + + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + //Proxy-Client-IP:apache 服务代理 + ipAddresses = request.getHeader("Proxy-Client-IP"); + } + + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + //WL-Proxy-Client-IP:weblogic 服务代理 + ipAddresses = request.getHeader("WL-Proxy-Client-IP"); + } + + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + //HTTP_CLIENT_IP:有些代理服务器 + ipAddresses = request.getHeader("HTTP_CLIENT_IP"); + } + + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + //X-Real-IP:nginx服务代理 + ipAddresses = request.getHeader("X-Real-IP"); + } + + //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP + if (ipAddresses != null && ipAddresses.length() != 0) { + ip = ipAddresses.split(",")[0]; + } + + //还是不能获取到,最后再通过request.getRemoteAddr();获取 + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + ip = request.getRemoteAddr(); + } + + return ip; + } + + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/Reflections.java b/exam-api/src/main/java/com/yf/exam/core/utils/Reflections.java new file mode 100644 index 0000000..4d50bf2 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/Reflections.java @@ -0,0 +1,324 @@ +/** + * Copyright (c) 2005-2012 springside.org.cn + */ +package com.yf.exam.core.utils; + +import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.springframework.util.Assert; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 反射工具类. + * 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * @author calvin + * @version 2016-01-15 + */ +@Log4j2 +public class Reflections { + + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + + /** + * 获取类的所有属性,包括父类 + * + * @param object + * @return + */ + public static Field[] getAllFields(Object object) { + Class clazz = object.getClass(); + List fieldList = new ArrayList<>(); + while (clazz != null) { + fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()))); + clazz = clazz.getSuperclass(); + } + Field[] fields = new Field[fieldList.size()]; + fieldList.toArray(fields); + return fields; + } + + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + public static Object invokeGetter(Object obj, String propertyName) { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")){ + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, Object value) { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i=0; i[] parameterTypes, + final Object[] args) { + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) { + throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]"); + } + + try { + return method.invoke(obj, args); + } catch (Exception e) { + throw convertReflectionExceptionToUnchecked(e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) { + Method method = getAccessibleMethodByName(obj, methodName); + if (method == null) { + throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]"); + } + + try { + return method.invoke(obj, args); + } catch (Exception e) { + throw convertReflectionExceptionToUnchecked(e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) { + Validate.notNull(obj, "object can't be null"); + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) { + try { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } catch (NoSuchFieldException e) {//NOSONAR + // Field不在当前类定义,继续向上转型 + continue;// new add + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) { + Validate.notNull(obj, "object can't be null"); + Validate.notBlank(methodName, "methodName can't be blank"); + + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { + try { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } catch (NoSuchMethodException e) { + // Method不在当前类定义,继续向上转型 + continue;// new add + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName) { + Validate.notNull(obj, "object can't be null"); + Validate.notBlank(methodName, "methodName can't be blank"); + + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) { + if (method.getName().equals(methodName)) { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier + .isFinal(field.getModifiers())) && !field.isAccessible()) { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + * eg. + * public UserDao extends HibernateDao + * + * @param clazz The class to introspect + * @return the first generic declaration, or Object.class if cannot be determined + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + * + * 如public UserDao extends HibernateDao + * + * @param clazz clazz The class to introspect + * @param index the Index of the generic ddeclaration,start from 0. + * @return the index generic declaration, or Object.class if cannot be determined + */ + public static Class getClassGenricType(final Class clazz, final int index) { + + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) { + log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) { + log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) { + log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) { + Assert.notNull(instance, "Instance must not be null"); + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) { + return new IllegalArgumentException(e); + } else if (e instanceof InvocationTargetException) { + return new RuntimeException(((InvocationTargetException) e).getTargetException()); + } else if (e instanceof RuntimeException) { + return (RuntimeException) e; + } + return new RuntimeException("Unexpected Checked Exception.", e); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/SpringUtils.java b/exam-api/src/main/java/com/yf/exam/core/utils/SpringUtils.java new file mode 100644 index 0000000..295c827 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/SpringUtils.java @@ -0,0 +1,32 @@ +package com.yf.exam.core.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * Spring获取工具 + * + * @author bool + * @date 2019-12-09 15:55 + */ +@Component +public class SpringUtils implements ApplicationContextAware { + + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + applicationContext = context; + } + + public static T getBean(Class tClass) { + return applicationContext.getBean(tClass); + } + + public static T getBean(String name, Class type) { + return applicationContext.getBean(name, type); + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/StringUtils.java b/exam-api/src/main/java/com/yf/exam/core/utils/StringUtils.java new file mode 100644 index 0000000..c0bc43b --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/StringUtils.java @@ -0,0 +1,39 @@ +package com.yf.exam.core.utils; + +import java.util.Map; + +/** + * 字符串常用工具类 + * @author bool + * @date 2019-05-15 11:40 + */ +public class StringUtils { + + /** + * 判断是否为空字符 + * @param str + * @return + */ + public static boolean isBlank(String str){ + return str==null || "".equals(str); + } + + + /** + * 将MAP转换成一个xml格式,格式为value... + * @param params + * @return + */ + public static String mapToXml(Map params){ + StringBuffer sb = new StringBuffer(""); + for(String key:params.keySet()){ + sb.append("<") + .append(key).append(">") + .append(params.get(key)) + .append(""); + } + + sb.append(""); + return sb.toString(); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/excel/ExportExcel.java b/exam-api/src/main/java/com/yf/exam/core/utils/excel/ExportExcel.java new file mode 100644 index 0000000..d98b557 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/excel/ExportExcel.java @@ -0,0 +1,402 @@ +/** + * Copyright © 2015-2020 JeePlus All rights reserved. + */ +package com.yf.exam.core.utils.excel; + +import com.google.common.collect.Lists; +import com.yf.exam.core.utils.Reflections; +import com.yf.exam.core.utils.excel.annotation.ExcelField; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Comment; +import org.apache.poi.ss.usermodel.DataFormat; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFRichTextString; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 导出Excel文件(导出“XLSX”格式,支持大数据量导出 @see org.apache.poi.ss.SpreadsheetVersion) + * @author jeeplus + * @version 2016-04-21 + */ +public class ExportExcel { + + private static Logger log = LoggerFactory.getLogger(ExportExcel.class); + + /** + * 工作薄对象 + */ + private SXSSFWorkbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 当前行号 + */ + private int rownum; + + /** + * 注解列表(Object[]{ ExcelField, Field/Method }) + */ + List annotationList = Lists.newArrayList(); + + /** + * 构造函数 + * @param title 表格标题,传“空值”,表示无标题 + * @param cls 实体对象,通过annotation.ExportField获取标题 + */ + public ExportExcel(String title, Class cls){ + this(title, cls, 1); + } + + /** + * 构造函数 + * @param title 表格标题,传“空值”,表示无标题 + * @param cls 实体对象,通过annotation.ExportField获取标题 + * @param type 导出类型(1:导出数据;2:导出模板) + * @param groups 导入分组 + */ + public ExportExcel(String title, Class cls, int type, int... groups){ + // Get annotation field + Field[] fs = cls.getDeclaredFields(); + for (Field f : fs){ + ExcelField ef = f.getAnnotation(ExcelField.class); + if (ef != null && (ef.type()==0 || ef.type()==type)){ + if (groups!=null && groups.length>0){ + boolean inGroup = false; + for (int g : groups){ + if (inGroup){ + break; + } + for (int efg : ef.groups()){ + if (g == efg){ + inGroup = true; + annotationList.add(new Object[]{ef, f}); + break; + } + } + } + }else{ + annotationList.add(new Object[]{ef, f}); + } + } + } + // Get annotation method + Method[] ms = cls.getDeclaredMethods(); + for (Method m : ms){ + ExcelField ef = m.getAnnotation(ExcelField.class); + if (ef != null && (ef.type()==0 || ef.type()==type)){ + if (groups!=null && groups.length>0){ + boolean inGroup = false; + for (int g : groups){ + if (inGroup){ + break; + } + for (int efg : ef.groups()){ + if (g == efg){ + inGroup = true; + annotationList.add(new Object[]{ef, m}); + break; + } + } + } + }else{ + annotationList.add(new Object[]{ef, m}); + } + } + } + // Field sorting + Collections.sort(annotationList, new Comparator() { + @Override + public int compare(Object[] o1, Object[] o2) { + return new Integer(((ExcelField)o1[0]).sort()).compareTo( + new Integer(((ExcelField)o2[0]).sort())); + } + }); + // Initialize + List headerList = Lists.newArrayList(); + for (Object[] os : annotationList){ + String t = ((ExcelField)os[0]).title(); + // 如果是导出,则去掉注释 + if (type==1){ + String[] ss = StringUtils.split(t, "**", 2); + if (ss.length==2){ + t = ss[0]; + } + } + headerList.add(t); + } + initialize(title, headerList); + } + /** + * 初始化函数 + * @param title 表格标题,传“空值”,表示无标题 + * @param headerList 表头列表 + */ + private void initialize(String title, List headerList) { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet("Export"); + this.styles = createStyles(wb); + // Create title + if (StringUtils.isNotBlank(title)){ + Row titleRow = sheet.createRow(rownum++); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), + titleRow.getRowNum(), titleRow.getRowNum(), headerList.size()-1)); + } + // Create header + if (headerList == null){ + throw new RuntimeException("headerList not null!"); + } + Row headerRow = sheet.createRow(rownum++); + headerRow.setHeightInPoints(16); + for (int i = 0; i < headerList.size(); i++) { + Cell cell = headerRow.createCell(i); + cell.setCellStyle(styles.get("header")); + String[] ss = StringUtils.split(headerList.get(i), "**", 2); + if (ss.length==2){ + cell.setCellValue(ss[0]); + Comment comment = this.sheet.createDrawingPatriarch().createCellComment( + new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6)); + comment.setString(new XSSFRichTextString(ss[1])); + cell.setCellComment(comment); + }else{ + cell.setCellValue(headerList.get(i)); + } + sheet.autoSizeColumn(i); + } + for (int i = 0; i < headerList.size(); i++) { + int colWidth = sheet.getColumnWidth(i)*2; + sheet.setColumnWidth(i, colWidth < 3000 ? 3000 : colWidth); + } + log.debug("Initialize success."); + } + + /** + * 创建表格样式 + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) { + Map styles = new HashMap<>(16); + + CellStyle style = wb.createCellStyle(); + style.setAlignment(CellStyle.ALIGN_CENTER); + style.setVerticalAlignment(CellStyle.VERTICAL_CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBoldweight(Font.BOLDWEIGHT_BOLD); + style.setFont(titleFont); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setVerticalAlignment(CellStyle.VERTICAL_CENTER); + style.setBorderRight(CellStyle.BORDER_THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(CellStyle.BORDER_THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(CellStyle.BORDER_THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(CellStyle.BORDER_THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(CellStyle.ALIGN_LEFT); + styles.put("data1", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(CellStyle.ALIGN_CENTER); + styles.put("data2", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(CellStyle.ALIGN_RIGHT); + styles.put("data3", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); +// style.setWrapText(true); + style.setAlignment(CellStyle.ALIGN_CENTER); + style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(CellStyle.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBoldweight(Font.BOLDWEIGHT_BOLD); + headerFont.setColor(IndexedColors.WHITE.getIndex()); + style.setFont(headerFont); + styles.put("header", style); + + return styles; + } + + /** + * 添加一行 + * @return 行对象 + */ + public Row addRow(){ + return sheet.createRow(rownum++); + } + + + /** + * 添加一个单元格 + * @param row 添加的行 + * @param column 添加列号 + * @param val 添加值 + * @return 单元格对象 + */ + public Cell addCell(Row row, int column, Object val){ + return this.addCell(row, column, val, 0, Class.class); + } + + /** + * 添加一个单元格 + * @param row 添加的行 + * @param column 添加列号 + * @param val 添加值 + * @param align 对齐方式(1:靠左;2:居中;3:靠右) + * @return 单元格对象 + */ + public Cell addCell(Row row, int column, Object val, int align, Class fieldType){ + Cell cell = row.createCell(column); + CellStyle style = styles.get("data"+(align>=1&&align<=3?align:"")); + try { + if (val == null){ + cell.setCellValue(""); + } else if (val instanceof String) { + cell.setCellValue((String) val); + } else if (val instanceof Integer) { + cell.setCellValue((Integer) val); + } else if (val instanceof Long) { + cell.setCellValue((Long) val); + } else if (val instanceof Double) { + cell.setCellValue((Double) val); + } else if (val instanceof Float) { + cell.setCellValue((Float) val); + } else if (val instanceof Date) { + DataFormat format = wb.createDataFormat(); + style.setDataFormat(format.getFormat("yyyy-MM-dd")); + cell.setCellValue((Date) val); + } else { + if (fieldType != Class.class){ + cell.setCellValue((String)fieldType.getMethod("setValue", Object.class).invoke(null, val)); + }else{ + cell.setCellValue((String)Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(), + "fieldtype."+val.getClass().getSimpleName()+"Type")).getMethod("setValue", Object.class).invoke(null, val)); + } + } + } catch (Exception ex) { + log.info("Set cell value ["+row.getRowNum()+","+column+"] error: " + ex.toString()); + cell.setCellValue(val.toString()); + } + cell.setCellStyle(style); + return cell; + } + + /** + * 添加数据(通过annotation.ExportField添加数据) + * @return list 数据列表 + */ + public ExportExcel setDataList(List list){ + for (E e : list){ + int colunm = 0; + Row row = this.addRow(); + StringBuilder sb = new StringBuilder(); + for (Object[] os : annotationList){ + ExcelField ef = (ExcelField)os[0]; + Object val = null; + try{ + if (StringUtils.isNotBlank(ef.value())){ + val = Reflections.invokeGetter(e, ef.value()); + }else{ + if (os[1] instanceof Field){ + val = Reflections.invokeGetter(e, ((Field)os[1]).getName()); + }else if (os[1] instanceof Method){ + val = Reflections.invokeMethod(e, ((Method)os[1]).getName(), new Class[] {}, new Object[] {}); + } + } + }catch(Exception ex) { + log.info(ex.toString()); + val = ""; + } + this.addCell(row, colunm++, val, ef.align(), ef.fieldType()); + sb.append(val + ", "); + } + log.debug("Write success: ["+row.getRowNum()+"] "+sb.toString()); + } + return this; + } + + /** + * 输出数据流 + * @param os 输出数据流 + */ + public ExportExcel write(OutputStream os) throws IOException{ + wb.write(os); + return this; + } + + /** + * 输出到客户端 + * @param fileName 输出文件名 + */ + public ExportExcel write(HttpServletResponse response, String fileName) throws IOException{ + response.reset(); + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setContentType("application/octet-stream; charset=utf-8"); + response.addHeader("Content-Disposition", "attachment; filename="+ URLEncoder.encode(fileName, "utf-8")); + write(response.getOutputStream()); + return this; + } + + /** + * 清理临时文件 + */ + public ExportExcel dispose(){ + wb.dispose(); + return this; + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/excel/ImportExcel.java b/exam-api/src/main/java/com/yf/exam/core/utils/excel/ImportExcel.java new file mode 100644 index 0000000..fca308a --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/excel/ImportExcel.java @@ -0,0 +1,303 @@ +/** + * Copyright © 2015-2020 JeePlus All rights reserved. + */ +package com.yf.exam.core.utils.excel; + +import com.google.common.collect.Lists; +import com.yf.exam.core.utils.Reflections; +import com.yf.exam.core.utils.excel.annotation.ExcelField; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hssf.usermodel.HSSFDateUtil; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +/** + * 导入Excel文件(支持“XLS”和“XLSX”格式) + * @author jeeplus + * @version 2016-03-10 + */ +public class ImportExcel { + + private static Logger log = LoggerFactory.getLogger(ImportExcel.class); + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 标题行号 + */ + private int headerNum; + + + + /** + * 构造函数 + * @param multipartFile 导入文件对象 + * @param headerNum 标题行号,数据行号=标题行号+1 + * @param sheetIndex 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ImportExcel(MultipartFile multipartFile, int headerNum, int sheetIndex) + throws InvalidFormatException, IOException { + this(multipartFile.getOriginalFilename(), multipartFile.getInputStream(), headerNum, sheetIndex); + } + + /** + * 构造函数 + * @param is 导入文件对象 + * @param headerNum 标题行号,数据行号=标题行号+1 + * @param sheetIndex 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ImportExcel(String fileName, InputStream is, int headerNum, int sheetIndex) + throws IOException { + if (StringUtils.isBlank(fileName)){ + throw new RuntimeException("导入文档为空!"); + }else if(fileName.toLowerCase().endsWith("xls")){ + this.wb = new HSSFWorkbook(is); + }else if(fileName.toLowerCase().endsWith("xlsx")){ + this.wb = new XSSFWorkbook(is); + }else{ + throw new RuntimeException("文档格式不正确!"); + } + if (this.wb.getNumberOfSheets() List getDataList(Class cls, int... groups) throws InstantiationException, IllegalAccessException{ + List annotationList = Lists.newArrayList(); + // Get annotation field + Field[] fs = cls.getDeclaredFields(); + for (Field f : fs){ + ExcelField ef = f.getAnnotation(ExcelField.class); + if (ef != null && (ef.type()==0 || ef.type()==2)){ + if (groups!=null && groups.length>0){ + boolean inGroup = false; + for (int g : groups){ + if (inGroup){ + break; + } + for (int efg : ef.groups()){ + if (g == efg){ + inGroup = true; + annotationList.add(new Object[]{ef, f}); + break; + } + } + } + }else{ + annotationList.add(new Object[]{ef, f}); + } + } + } + // Get annotation method + Method[] ms = cls.getDeclaredMethods(); + for (Method m : ms){ + ExcelField ef = m.getAnnotation(ExcelField.class); + if (ef != null && (ef.type()==0 || ef.type()==2)){ + if (groups!=null && groups.length>0){ + boolean inGroup = false; + for (int g : groups){ + if (inGroup){ + break; + } + for (int efg : ef.groups()){ + if (g == efg){ + inGroup = true; + annotationList.add(new Object[]{ef, m}); + break; + } + } + } + }else{ + annotationList.add(new Object[]{ef, m}); + } + } + } + // Field sorting + Collections.sort(annotationList, new Comparator() { + @Override + public int compare(Object[] o1, Object[] o2) { + return new Integer(((ExcelField)o1[0]).sort()).compareTo( + new Integer(((ExcelField)o2[0]).sort())); + } + }); + // Get excel data + List dataList = Lists.newArrayList(); + for (int i = this.getDataRowNum(); i < this.getLastDataRowNum(); i++) { + E e = (E)cls.newInstance(); + int column = 0; + Row row = this.getRow(i); + StringBuilder sb = new StringBuilder(); + for (Object[] os : annotationList){ + Object val = this.getCellValue(row, column++); + if (val != null){ + ExcelField ef = (ExcelField)os[0]; + // Get param type and type cast + Class valType = Class.class; + if (os[1] instanceof Field){ + valType = ((Field)os[1]).getType(); + }else if (os[1] instanceof Method){ + Method method = ((Method)os[1]); + if ("get".equals(method.getName().substring(0, 3))){ + valType = method.getReturnType(); + }else if("set".equals(method.getName().substring(0, 3))){ + valType = ((Method)os[1]).getParameterTypes()[0]; + } + } + //log.debug("Import value type: ["+i+","+column+"] " + valType); + try { + //如果导入的java对象,需要在这里自己进行变换。 + if (valType == String.class){ + String s = String.valueOf(val.toString()); + if(StringUtils.endsWith(s, ".0")){ + val = StringUtils.substringBefore(s, ".0"); + }else{ + val = String.valueOf(val.toString()); + } + }else if (valType == Integer.class){ + val = Double.valueOf(val.toString()).intValue(); + }else if (valType == Long.class){ + val = Double.valueOf(val.toString()).longValue(); + }else if (valType == Double.class){ + val = Double.valueOf(val.toString()); + }else if (valType == Float.class){ + val = Float.valueOf(val.toString()); + }else if (valType == Date.class){ + SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); + val=sdf.parse(val.toString()); + }else{ + if (ef.fieldType() != Class.class){ + val = ef.fieldType().getMethod("getValue", String.class).invoke(null, val.toString()); + }else{ + val = Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(), + "fieldtype."+valType.getSimpleName()+"Type")).getMethod("getValue", String.class).invoke(null, val.toString()); + } + } + } catch (Exception ex) { + log.info("Get cell value ["+i+","+column+"] error: " + ex.toString()); + val = null; + } + // set entity value + if (os[1] instanceof Field){ + Reflections.invokeSetter(e, ((Field)os[1]).getName(), val); + }else if (os[1] instanceof Method){ + String mthodName = ((Method)os[1]).getName(); + if ("get".equals(mthodName.substring(0, 3))){ + mthodName = "set"+StringUtils.substringAfter(mthodName, "get"); + } + Reflections.invokeMethod(e, mthodName, new Class[] {valType}, new Object[] {val}); + } + } + sb.append(val+", "); + } + dataList.add(e); + log.debug("Read success: ["+i+"] "+sb.toString()); + } + return dataList; + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/excel/annotation/ExcelField.java b/exam-api/src/main/java/com/yf/exam/core/utils/excel/annotation/ExcelField.java new file mode 100644 index 0000000..add0d1c --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/excel/annotation/ExcelField.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2015-2020 JeePlus All rights reserved. + */ +package com.yf.exam.core.utils.excel.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解定义 + * @author jeeplus + * @version 2016-03-10 + */ +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExcelField { + + /** + * 导出字段名(默认调用当前字段的“get”方法,如指定导出字段为对象,请填写“对象名.对象属性”,例:“area.name”、“office.name”) + */ + String value() default ""; + + /** + * 导出字段标题(需要添加批注请用“**”分隔,标题**批注,仅对导出模板有效) + */ + String title(); + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + int type() default 0; + + /** + * 导出字段对齐方式(0:自动;1:靠左;2:居中;3:靠右) + */ + int align() default 0; + + /** + * 导出字段字段排序(升序) + */ + int sort() default 0; + + /** + * 如果是字典类型,请设置字典的type值 + */ + String dictType() default ""; + + /** + * 反射类型 + */ + Class fieldType() default Class.class; + + /** + * 字段归属组(根据分组导出导入) + */ + int[] groups() default {}; +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/excel/fieldtype/ListType.java b/exam-api/src/main/java/com/yf/exam/core/utils/excel/fieldtype/ListType.java new file mode 100644 index 0000000..9369888 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/excel/fieldtype/ListType.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2015-2020 JeePlus All rights reserved. + */ +package com.yf.exam.core.utils.excel.fieldtype; + +import com.google.common.collect.Lists; +import com.yf.exam.core.utils.StringUtils; + +import java.util.List; + +/** + * 字段类型转换 + * @author jeeplus + * @version 2016-5-29 + */ +public class ListType { + + /** + * 获取对象值(导入) + */ + public static Object getValue(String val) { + List list = Lists.newArrayList(); + if(!StringUtils.isBlank(val)) { + for (String s : val.split(",")) { + list.add(s); + } + } + return list; + } + + /** + * 设置对象值(导出) + */ + public static String setValue(Object val) { + if (val != null){ + List list = (List)val; + StringBuffer sb = null; + for (String item: list){ + if(StringUtils.isBlank(item)){ + continue; + } + if(sb == null){ + sb = new StringBuffer(item); + }else{ + sb.append(",").append(item); + } + } + + if(sb!=null) { + return sb.toString().replace("[]", ""); + } + } + return ""; + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/file/Md5Util.java b/exam-api/src/main/java/com/yf/exam/core/utils/file/Md5Util.java new file mode 100644 index 0000000..d5d33cf --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/file/Md5Util.java @@ -0,0 +1,37 @@ +package com.yf.exam.core.utils.file; + +import java.security.MessageDigest; + + +/** + * MD5工具类 + * ClassName: MD5Util
+ * date: 2018年1月13日 下午6:54:53
+ * + * @author Bool + * @version + */ +public class Md5Util { + + + /** + * 简单MD5 + * @param str + * @return + */ + public static String md5(String str) { + + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] array = md.digest(str.getBytes("UTF-8")); + StringBuilder sb = new StringBuilder(); + for (byte item : array) { + sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); + } + return sb.toString(); + }catch(Exception e) { + return null; + } + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/passwd/PassHandler.java b/exam-api/src/main/java/com/yf/exam/core/utils/passwd/PassHandler.java new file mode 100644 index 0000000..1af239e --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/passwd/PassHandler.java @@ -0,0 +1,57 @@ +package com.yf.exam.core.utils.passwd; + + +import com.yf.exam.core.utils.file.Md5Util; +import org.apache.commons.lang3.RandomStringUtils; + +/** + * 通用的密码处理类,用于生成密码和校验密码 + * ClassName: PassGenerator
+ * date: 2017年12月13日 下午7:13:03
+ * + * @author Bool + * @version + */ +public class PassHandler { + + /** + * checkPass:校验密码是否一致 + * @author Bool + * @param inputPass 用户传入的密码 + * @param salt 数据库保存的密码随机码 + * @param pass 数据库保存的密码MD5 + * @return + */ + public static boolean checkPass(String inputPass , String salt , String pass){ + String pwdMd5 = Md5Util.md5(inputPass); + return Md5Util.md5(pwdMd5 + salt).equals(pass); + } + + + /** + * + * buildPassword:用于用户注册时产生一个密码 + * @author Bool + * @param inputPass 输入的密码 + * @return PassInfo 返回一个密码对象,记得保存 + */ + public static PassInfo buildPassword(String inputPass) { + + //产生一个6位数的随机码 + String salt = RandomStringUtils.randomAlphabetic(6); + //加密后的密码 + String encryptPassword = Md5Util.md5(Md5Util.md5(inputPass)+salt); + //返回对象 + return new PassInfo(salt,encryptPassword); + } + + + public static void main(String[] args) { + + PassInfo info = buildPassword("190601"); + + System.out.println(info.getPassword()); + System.out.println(info.getSalt()); + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/core/utils/passwd/PassInfo.java b/exam-api/src/main/java/com/yf/exam/core/utils/passwd/PassInfo.java new file mode 100644 index 0000000..3634fb6 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/core/utils/passwd/PassInfo.java @@ -0,0 +1,38 @@ +package com.yf.exam.core.utils.passwd; + +/** + * 密码实体 + * ClassName: PassInfo
+ * date: 2018年2月13日 下午7:13:50
+ * + * @author Bool + * @version + */ +public class PassInfo { + + //密码随机串码 + private String salt; + + //MD5后的密码 + private String password; + + public PassInfo(String salt, String password) { + super(); + this.salt = salt; + this.password = password; + } + + public String getSalt() { + return salt; + } + public void setSalt(String salt) { + this.salt = salt; + } + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } +} + diff --git a/exam-api/src/main/java/com/yf/exam/modules/Constant.java b/exam-api/src/main/java/com/yf/exam/modules/Constant.java new file mode 100644 index 0000000..20c5726 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/Constant.java @@ -0,0 +1,14 @@ +package com.yf.exam.modules; + + +/** + * 通用常量 + * @author bool + */ +public class Constant { + + /** + * 会话 + */ + public static final String TOKEN = "token"; +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/controller/ExamController.java b/exam-api/src/main/java/com/yf/exam/modules/exam/controller/ExamController.java new file mode 100644 index 0000000..e5583f2 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/controller/ExamController.java @@ -0,0 +1,151 @@ +package com.yf.exam.modules.exam.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import com.yf.exam.core.api.dto.BaseIdReqDTO; +import com.yf.exam.core.api.dto.BaseIdsReqDTO; +import com.yf.exam.core.api.dto.BaseStateReqDTO; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.exam.dto.ExamDTO; +import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO; +import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; +import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; +import com.yf.exam.modules.exam.entity.Exam; +import com.yf.exam.modules.exam.service.ExamService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; + +/** +*

+* 考试控制器 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Api(tags={"考试"}) +@RestController +@RequestMapping("/exam/api/exam/exam") +public class ExamController extends BaseController { + + @Autowired + private ExamService baseService; + + /** + * 添加或修改 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "添加或修改") + @RequestMapping(value = "/save", method = { RequestMethod.POST}) + public ApiRest save(@RequestBody ExamSaveReqDTO reqDTO) { + //复制参数 + baseService.save(reqDTO); + return super.success(); + } + + /** + * 批量删除 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "批量删除") + @RequestMapping(value = "/delete", method = { RequestMethod.POST}) + public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) { + //根据ID删除 + baseService.removeByIds(reqDTO.getIds()); + return super.success(); + } + + /** + * 查找详情 + * @param reqDTO + * @return + */ + @ApiOperation(value = "查找详情") + @RequestMapping(value = "/detail", method = { RequestMethod.POST}) + public ApiRest find(@RequestBody BaseIdReqDTO reqDTO) { + ExamSaveReqDTO dto = baseService.findDetail(reqDTO.getId()); + return super.success(dto); + } + + /** + * 查找详情 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "查找详情") + @RequestMapping(value = "/state", method = { RequestMethod.POST}) + public ApiRest state(@RequestBody BaseStateReqDTO reqDTO) { + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().in(Exam::getId, reqDTO.getIds()); + Exam exam = new Exam(); + exam.setState(reqDTO.getState()); + exam.setUpdateTime(new Date()); + + baseService.update(exam, wrapper); + return super.success(); + } + + + /** + * 分页查找 + * @param reqDTO + * @return + */ + @ApiOperation(value = "考试视角") + @RequestMapping(value = "/online-paging", method = { RequestMethod.POST}) + public ApiRest> myPaging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.onlinePaging(reqDTO); + return super.success(page); + } + + /** + * 分页查找 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "分页查找") + @RequestMapping(value = "/paging", method = { RequestMethod.POST}) + public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.paging(reqDTO); + + return super.success(page); + } + + + /** + * 分页查找 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "待阅试卷") + @RequestMapping(value = "/review-paging", method = { RequestMethod.POST}) + public ApiRest> reviewPaging(@RequestBody PagingReqDTO reqDTO) { + //分页查询并转换 + IPage page = baseService.reviewPaging(reqDTO); + return super.success(page); + } + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ExamDTO.java b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ExamDTO.java new file mode 100644 index 0000000..90f86f1 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ExamDTO.java @@ -0,0 +1,101 @@ +package com.yf.exam.modules.exam.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.yf.exam.modules.paper.enums.ExamState; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** +*

+* 考试数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Data +@ApiModel(value="考试", description="考试") +public class ExamDTO implements Serializable { + + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "考试名称", required=true) + private String title; + + @ApiModelProperty(value = "考试描述", required=true) + private String content; + + @ApiModelProperty(value = "1公开2部门3定员", required=true) + private Integer openType; + + @ApiModelProperty(value = "考试状态", required=true) + private Integer state; + + @ApiModelProperty(value = "是否限时", required=true) + private Boolean timeLimit; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "开始时间", required=true) + private Date startTime; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "结束时间", required=true) + private Date endTime; + + @ApiModelProperty(value = "创建时间", required=true) + private Date createTime; + + @ApiModelProperty(value = "更新时间", required=true) + private Date updateTime; + + @ApiModelProperty(value = "总分数", required=true) + private Integer totalScore; + + @ApiModelProperty(value = "总时长(分钟)", required=true) + private Integer totalTime; + + @ApiModelProperty(value = "及格分数", required=true) + private Integer qualifyScore; + + + + + /** + * 是否结束 + * @return + */ + public Integer getState(){ + + if(this.timeLimit!=null && this.timeLimit){ + + if(System.currentTimeMillis() < startTime.getTime() ){ + return ExamState.READY_START; + } + + if(System.currentTimeMillis() > endTime.getTime()){ + return ExamState.OVERDUE; + } + + if(System.currentTimeMillis() > startTime.getTime() + && System.currentTimeMillis() < endTime.getTime() + && !ExamState.DISABLED.equals(this.state)){ + return ExamState.ENABLE; + } + + } + + return this.state; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ExamDepartDTO.java b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ExamDepartDTO.java new file mode 100644 index 0000000..d0e54b7 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ExamDepartDTO.java @@ -0,0 +1,33 @@ +package com.yf.exam.modules.exam.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 考试部门数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-03 17:24 +*/ +@Data +@ApiModel(value="考试部门", description="考试部门") +public class ExamDepartDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "考试ID", required=true) + private String examId; + + @ApiModelProperty(value = "部门ID", required=true) + private String departId; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ExamRepoDTO.java b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ExamRepoDTO.java new file mode 100644 index 0000000..7244e9a --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ExamRepoDTO.java @@ -0,0 +1,51 @@ +package com.yf.exam.modules.exam.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 考试题库数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +@Data +@ApiModel(value="考试题库", description="考试题库") +public class ExamRepoDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "考试ID", required=true) + private String examId; + + @ApiModelProperty(value = "题库ID", required=true) + private String repoId; + + @ApiModelProperty(value = "单选题数量", required=true) + private Integer radioCount; + + @ApiModelProperty(value = "单选题分数", required=true) + private Integer radioScore; + + @ApiModelProperty(value = "多选题数量", required=true) + private Integer multiCount; + + @ApiModelProperty(value = "多选题分数", required=true) + private Integer multiScore; + + @ApiModelProperty(value = "判断题数量", required=true) + private Integer judgeCount; + + @ApiModelProperty(value = "判断题分数", required=true) + private Integer judgeScore; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ext/ExamRepoExtDTO.java b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ext/ExamRepoExtDTO.java new file mode 100644 index 0000000..a566fec --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/ext/ExamRepoExtDTO.java @@ -0,0 +1,32 @@ +package com.yf.exam.modules.exam.dto.ext; + +import com.yf.exam.modules.exam.dto.ExamRepoDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** +*

+* 考试题库数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +@Data +@ApiModel(value="考试题库扩展响应类", description="考试题库扩展响应类") +public class ExamRepoExtDTO extends ExamRepoDTO { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "单选题总量", required=true) + private Integer totalRadio; + + @ApiModelProperty(value = "多选题总量", required=true) + private Integer totalMulti; + + @ApiModelProperty(value = "判断题总量", required=true) + private Integer totalJudge; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/dto/request/ExamSaveReqDTO.java b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/request/ExamSaveReqDTO.java new file mode 100644 index 0000000..5c1a95b --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/request/ExamSaveReqDTO.java @@ -0,0 +1,32 @@ +package com.yf.exam.modules.exam.dto.request; + +import com.yf.exam.modules.exam.dto.ExamDTO; +import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** +*

+* 考试保存请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Data +@ApiModel(value="考试保存请求类", description="考试保存请求类") +public class ExamSaveReqDTO extends ExamDTO { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "题库列表", required=true) + private List repoList; + + @ApiModelProperty(value = "考试部门列表", required=true) + private List departIds; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/dto/response/ExamOnlineRespDTO.java b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/response/ExamOnlineRespDTO.java new file mode 100644 index 0000000..edbc5ce --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/response/ExamOnlineRespDTO.java @@ -0,0 +1,22 @@ +package com.yf.exam.modules.exam.dto.response; + +import com.yf.exam.modules.exam.dto.ExamDTO; +import io.swagger.annotations.ApiModel; +import lombok.Data; + +/** +*

+* 考试分页响应类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Data +@ApiModel(value="在线考试分页响应类", description="在线考试分页响应类") +public class ExamOnlineRespDTO extends ExamDTO { + + private static final long serialVersionUID = 1L; + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/dto/response/ExamReviewRespDTO.java b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/response/ExamReviewRespDTO.java new file mode 100644 index 0000000..7d9ad36 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/dto/response/ExamReviewRespDTO.java @@ -0,0 +1,31 @@ +package com.yf.exam.modules.exam.dto.response; + +import com.yf.exam.modules.exam.dto.ExamDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** +*

+* 考试分页响应类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Data +@ApiModel(value="阅卷分页响应类", description="阅卷分页响应类") +public class ExamReviewRespDTO extends ExamDTO { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "考试人数", required=true) + private Integer examUser; + + @ApiModelProperty(value = "待阅试卷", required=true) + private Integer unreadPaper; + + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/entity/Exam.java b/exam-api/src/main/java/com/yf/exam/modules/exam/entity/Exam.java new file mode 100644 index 0000000..e198c69 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/entity/Exam.java @@ -0,0 +1,100 @@ +package com.yf.exam.modules.exam.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; +import java.util.Date; + +/** +*

+* 考试实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Data +@TableName("el_exam") +public class Exam extends Model { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 考试名称 + */ + private String title; + + /** + * 考试描述 + */ + private String content; + + /** + * 1公开2部门3定员 + */ + @TableField("open_type") + private Integer openType; + + /** + * 考试状态 + */ + private Integer state; + + /** + * 是否限时 + */ + @TableField("time_limit") + private Boolean timeLimit; + + /** + * 开始时间 + */ + @TableField("start_time") + private Date startTime; + + /** + * 结束时间 + */ + @TableField("end_time") + private Date endTime; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + + /** + * 总分数 + */ + @TableField("total_score") + private Integer totalScore; + + /** + * 总时长(分钟) + */ + @TableField("total_time") + private Integer totalTime; + + /** + * 及格分数 + */ + @TableField("qualify_score") + private Integer qualifyScore; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/entity/ExamDepart.java b/exam-api/src/main/java/com/yf/exam/modules/exam/entity/ExamDepart.java new file mode 100644 index 0000000..19a4238 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/entity/ExamDepart.java @@ -0,0 +1,42 @@ +package com.yf.exam.modules.exam.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +/** +*

+* 考试部门实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-03 17:24 +*/ +@Data +@TableName("el_exam_depart") +public class ExamDepart extends Model { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 考试ID + */ + @TableField("exam_id") + private String examId; + + /** + * 部门ID + */ + @TableField("depart_id") + private String departId; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/entity/ExamRepo.java b/exam-api/src/main/java/com/yf/exam/modules/exam/entity/ExamRepo.java new file mode 100644 index 0000000..3884051 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/entity/ExamRepo.java @@ -0,0 +1,78 @@ +package com.yf.exam.modules.exam.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +/** +*

+* 考试题库实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +@Data +@TableName("el_exam_repo") +public class ExamRepo extends Model { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 考试ID + */ + @TableField("exam_id") + private String examId; + + /** + * 题库ID + */ + @TableField("repo_id") + private String repoId; + + /** + * 单选题数量 + */ + @TableField("radio_count") + private Integer radioCount; + + /** + * 单选题分数 + */ + @TableField("radio_score") + private Integer radioScore; + + /** + * 多选题数量 + */ + @TableField("multi_count") + private Integer multiCount; + + /** + * 多选题分数 + */ + @TableField("multi_score") + private Integer multiScore; + + /** + * 判断题数量 + */ + @TableField("judge_count") + private Integer judgeCount; + + /** + * 判断题分数 + */ + @TableField("judge_score") + private Integer judgeScore; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/mapper/ExamDepartMapper.java b/exam-api/src/main/java/com/yf/exam/modules/exam/mapper/ExamDepartMapper.java new file mode 100644 index 0000000..0c1f39a --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/mapper/ExamDepartMapper.java @@ -0,0 +1,15 @@ +package com.yf.exam.modules.exam.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.exam.entity.ExamDepart; +/** +*

+* 考试部门Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-09-03 17:24 +*/ +public interface ExamDepartMapper extends BaseMapper { + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/mapper/ExamMapper.java b/exam-api/src/main/java/com/yf/exam/modules/exam/mapper/ExamMapper.java new file mode 100644 index 0000000..1ce4504 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/mapper/ExamMapper.java @@ -0,0 +1,45 @@ +package com.yf.exam.modules.exam.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yf.exam.modules.exam.dto.ExamDTO; +import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; +import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; +import com.yf.exam.modules.exam.entity.Exam; +import org.apache.ibatis.annotations.Param; + +/** +*

+* 考试Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +public interface ExamMapper extends BaseMapper { + + /** + * 查找分页内容 + * @param page + * @param query + * @return + */ + IPage paging(Page page, @Param("query") ExamDTO query); + + /** + * 查找分页内容 + * @param page + * @param query + * @return + */ + IPage reviewPaging(Page page, @Param("query") ExamDTO query); + + /** + * 在线考试分页响应类-考生视角 + * @param page + * @param query + * @return + */ + IPage online(Page page, @Param("query") ExamDTO query); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/mapper/ExamRepoMapper.java b/exam-api/src/main/java/com/yf/exam/modules/exam/mapper/ExamRepoMapper.java new file mode 100644 index 0000000..83407df --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/mapper/ExamRepoMapper.java @@ -0,0 +1,26 @@ +package com.yf.exam.modules.exam.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +import com.yf.exam.modules.exam.entity.ExamRepo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +*

+* 考试题库Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +public interface ExamRepoMapper extends BaseMapper { + + /** + * 查找考试题库列表 + * @param examId + * @return + */ + List listByExam(@Param("examId") String examId); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/service/ExamDepartService.java b/exam-api/src/main/java/com/yf/exam/modules/exam/service/ExamDepartService.java new file mode 100644 index 0000000..301c010 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/service/ExamDepartService.java @@ -0,0 +1,32 @@ +package com.yf.exam.modules.exam.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.modules.exam.entity.ExamDepart; + +import java.util.List; + +/** +*

+* 考试部门业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-03 17:24 +*/ +public interface ExamDepartService extends IService { + + /** + * 保存全部 + * @param examId + * @param departs + */ + void saveAll(String examId, List departs); + + + /** + * 根据考试查找对应的部门 + * @param examId + * @return + */ + List listByExam(String examId); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/service/ExamRepoService.java b/exam-api/src/main/java/com/yf/exam/modules/exam/service/ExamRepoService.java new file mode 100644 index 0000000..78b4ec1 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/service/ExamRepoService.java @@ -0,0 +1,40 @@ +package com.yf.exam.modules.exam.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +import com.yf.exam.modules.exam.entity.ExamRepo; + +import java.util.List; + +/** +*

+* 考试题库业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +public interface ExamRepoService extends IService { + + + /** + * 保存全部 + * @param examId + * @param list + */ + void saveAll(String examId, List list); + + /** + * 查找考试题库列表 + * @param examId + * @return + */ + List listByExam(String examId); + + /** + * 清理脏数据 + * @param examId + */ + void clear(String examId); + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/service/ExamService.java b/exam-api/src/main/java/com/yf/exam/modules/exam/service/ExamService.java new file mode 100644 index 0000000..3f75664 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/service/ExamService.java @@ -0,0 +1,64 @@ +package com.yf.exam.modules.exam.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.exam.dto.ExamDTO; +import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO; +import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; +import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; +import com.yf.exam.modules.exam.entity.Exam; + +/** +*

+* 考试业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +public interface ExamService extends IService { + + /** + * 保存考试信息 + * @param reqDTO + */ + void save(ExamSaveReqDTO reqDTO); + + /** + * 查找考试详情 + * @param id + * @return + */ + ExamSaveReqDTO findDetail(String id); + + /** + * 查找考试详情--简要信息 + * @param id + * @return + */ + ExamDTO findById(String id); + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + + /** + * 在线考试分页响应类-考生视角 + * @param reqDTO + * @return + */ + IPage onlinePaging(PagingReqDTO reqDTO); + + + /** + * 待阅试卷列表 + * @param reqDTO + * @return + */ + IPage reviewPaging(PagingReqDTO reqDTO); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/service/impl/ExamDepartServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/exam/service/impl/ExamDepartServiceImpl.java new file mode 100644 index 0000000..37ca2ff --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/service/impl/ExamDepartServiceImpl.java @@ -0,0 +1,66 @@ +package com.yf.exam.modules.exam.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.exception.ServiceException; +import com.yf.exam.modules.exam.entity.ExamDepart; +import com.yf.exam.modules.exam.mapper.ExamDepartMapper; +import com.yf.exam.modules.exam.service.ExamDepartService; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; + +/** +*

+* 考试部门业务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-03 17:24 +*/ +@Service +public class ExamDepartServiceImpl extends ServiceImpl implements ExamDepartService { + + @Override + public void saveAll(String examId, List departs) { + + // 先删除 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(ExamDepart::getExamId, examId); + this.remove(wrapper); + + // 再增加 + if(CollectionUtils.isEmpty(departs)){ + throw new ServiceException(1, "请至少选择选择一个部门!!"); + } + List list = new ArrayList<>(); + + for(String id: departs){ + ExamDepart depart = new ExamDepart(); + depart.setDepartId(id); + depart.setExamId(examId); + list.add(depart); + } + + this.saveBatch(list); + } + + @Override + public List listByExam(String examId) { + // 先删除 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(ExamDepart::getExamId, examId); + List list = this.list(wrapper); + List ids = new ArrayList<>(); + if(!CollectionUtils.isEmpty(list)){ + for(ExamDepart item: list){ + ids.add(item.getDepartId()); + } + } + + return ids; + + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/service/impl/ExamRepoServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/exam/service/impl/ExamRepoServiceImpl.java new file mode 100644 index 0000000..ea630e6 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/service/impl/ExamRepoServiceImpl.java @@ -0,0 +1,67 @@ +package com.yf.exam.modules.exam.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.exception.ServiceException; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +import com.yf.exam.modules.exam.entity.ExamRepo; +import com.yf.exam.modules.exam.mapper.ExamRepoMapper; +import com.yf.exam.modules.exam.service.ExamRepoService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.util.List; + +/** +*

+* 考试题库业务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-05 11:14 +*/ +@Service +public class ExamRepoServiceImpl extends ServiceImpl implements ExamRepoService { + + + @Transactional(rollbackFor = Exception.class) + @Override + public void saveAll(String examId, List list) { + + // 先删除 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(ExamRepo::getExamId, examId); + this.remove(wrapper); + + // 再增加 + if(CollectionUtils.isEmpty(list)){ + throw new ServiceException(1, "必须选择题库!"); + } + List repos = BeanMapper.mapList(list, ExamRepo.class); + for(ExamRepo item: repos){ + item.setExamId(examId); + item.setId(IdWorker.getIdStr()); + } + + this.saveBatch(repos); + } + + @Override + public List listByExam(String examId) { + return baseMapper.listByExam(examId); + } + + @Override + public void clear(String examId) { + + // 先删除 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(ExamRepo::getExamId, examId); + this.remove(wrapper); + } + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/exam/service/impl/ExamServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/exam/service/impl/ExamServiceImpl.java new file mode 100644 index 0000000..7451bd9 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/exam/service/impl/ExamServiceImpl.java @@ -0,0 +1,194 @@ +package com.yf.exam.modules.exam.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.enums.OpenType; +import com.yf.exam.core.exception.ServiceException; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.exam.dto.ExamDTO; +import com.yf.exam.modules.exam.dto.ExamRepoDTO; +import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO; +import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO; +import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO; +import com.yf.exam.modules.exam.entity.Exam; +import com.yf.exam.modules.exam.mapper.ExamMapper; +import com.yf.exam.modules.exam.service.ExamDepartService; +import com.yf.exam.modules.exam.service.ExamRepoService; +import com.yf.exam.modules.exam.service.ExamService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** +*

+* 考试业务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-07-25 16:18 +*/ +@Service +public class ExamServiceImpl extends ServiceImpl implements ExamService { + + + @Autowired + private ExamRepoService examRepoService; + + @Autowired + private ExamDepartService examDepartService; + + @Override + public void save(ExamSaveReqDTO reqDTO) { + + // ID + String id = reqDTO.getId(); + + if(StringUtils.isBlank(id)){ + id = IdWorker.getIdStr(); + } + + //复制参数 + Exam entity = new Exam(); + + // 计算分值 + this.calcScore(reqDTO); + + + // 复制基本数据 + BeanMapper.copy(reqDTO, entity); + entity.setId(id); + + // 修复状态 + if (reqDTO.getTimeLimit()!=null + && !reqDTO.getTimeLimit() + && reqDTO.getState()!=null + && reqDTO.getState() == 2) { + entity.setState(0); + } else { + entity.setState(reqDTO.getState()); + } + + // 题库组卷 + try { + examRepoService.saveAll(id, reqDTO.getRepoList()); + }catch (DuplicateKeyException e){ + throw new ServiceException(1, "不能选择重复的题库!"); + } + + + // 开放的部门 + if(OpenType.DEPT_OPEN.equals(reqDTO.getOpenType())){ + examDepartService.saveAll(id, reqDTO.getDepartIds()); + } + + this.saveOrUpdate(entity); + + } + + @Override + public ExamSaveReqDTO findDetail(String id) { + ExamSaveReqDTO respDTO = new ExamSaveReqDTO(); + Exam exam = this.getById(id); + BeanMapper.copy(exam, respDTO); + + // 考试部门 + List departIds = examDepartService.listByExam(id); + respDTO.setDepartIds(departIds); + + // 题库 + List repos = examRepoService.listByExam(id); + respDTO.setRepoList(repos); + + return respDTO; + } + + @Override + public ExamDTO findById(String id) { + ExamDTO respDTO = new ExamDTO(); + Exam exam = this.getById(id); + BeanMapper.copy(exam, respDTO); + return respDTO; + } + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + //创建分页对象 + Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize()); + + //转换结果 + IPage pageData = baseMapper.paging(page, reqDTO.getParams()); + return pageData; + } + + @Override + public IPage onlinePaging(PagingReqDTO reqDTO) { + + + // 创建分页对象 + Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize()); + + // 查找分页 + IPage pageData = baseMapper.online(page, reqDTO.getParams()); + + return pageData; + } + + @Override + public IPage reviewPaging(PagingReqDTO reqDTO) { + // 创建分页对象 + Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize()); + + // 查找分页 + IPage pageData = baseMapper.reviewPaging(page, reqDTO.getParams()); + + return pageData; + } + + + /** + * 计算分值 + * @param reqDTO + */ + private void calcScore(ExamSaveReqDTO reqDTO){ + + // 主观题分数 + int objScore = 0; + + // 题库组卷 + List repoList = reqDTO.getRepoList(); + + for(ExamRepoDTO item: repoList){ + if(item.getRadioCount()!=null + && item.getRadioCount()>0 + && item.getRadioScore()!=null + && item.getRadioScore()>0){ + objScore+=item.getRadioCount()*item.getRadioScore(); + } + if(item.getMultiCount()!=null + && item.getMultiCount()>0 + && item.getMultiScore()!=null + && item.getMultiScore()>0){ + objScore+=item.getMultiCount()*item.getMultiScore(); + } + if(item.getJudgeCount()!=null + && item.getJudgeCount()>0 + && item.getJudgeScore()!=null + && item.getJudgeScore()>0){ + objScore+=item.getJudgeCount()*item.getJudgeScore(); + } + } + + + + reqDTO.setTotalScore(objScore); + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/controller/PaperController.java b/exam-api/src/main/java/com/yf/exam/modules/paper/controller/PaperController.java new file mode 100644 index 0000000..5549670 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/controller/PaperController.java @@ -0,0 +1,156 @@ +package com.yf.exam.modules.paper.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import com.yf.exam.core.api.dto.BaseIdReqDTO; +import com.yf.exam.core.api.dto.BaseIdRespDTO; +import com.yf.exam.core.api.dto.BaseIdsReqDTO; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.paper.dto.PaperDTO; +import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; +import com.yf.exam.modules.paper.dto.request.PaperAnswerDTO; +import com.yf.exam.modules.paper.dto.request.PaperCreateReqDTO; +import com.yf.exam.modules.paper.dto.request.PaperListReqDTO; +import com.yf.exam.modules.paper.dto.request.PaperQuQueryDTO; +import com.yf.exam.modules.paper.dto.response.ExamDetailRespDTO; +import com.yf.exam.modules.paper.dto.response.ExamResultRespDTO; +import com.yf.exam.modules.paper.dto.response.PaperListRespDTO; +import com.yf.exam.modules.paper.entity.Paper; +import com.yf.exam.modules.paper.service.PaperService; +import com.yf.exam.modules.user.UserUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** +*

+* 试卷控制器 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 16:33 +*/ +@Api(tags={"试卷"}) +@RestController +@RequestMapping("/exam/api/paper/paper") +public class PaperController extends BaseController { + + @Autowired + private PaperService baseService; + + /** + * 分页查找 + * @param reqDTO + * @return + */ + @ApiOperation(value = "分页查找") + @RequestMapping(value = "/paging", method = { RequestMethod.POST}) + public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { + //分页查询并转换 + IPage page = baseService.paging(reqDTO); + return super.success(page); + } + + + + /** + * 创建试卷 + * @param reqDTO + * @return + */ + @ApiOperation(value = "创建试卷") + @RequestMapping(value = "/create-paper", method = { RequestMethod.POST}) + public ApiRest save(@RequestBody PaperCreateReqDTO reqDTO) { + //复制参数 + String paperId = baseService.createPaper(UserUtils.getUserId(), reqDTO.getExamId()); + return super.success(new BaseIdRespDTO(paperId)); + } + + /** + * 批量删除 + * @param reqDTO + * @return + */ + @ApiOperation(value = "试卷详情") + @RequestMapping(value = "/paper-detail", method = { RequestMethod.POST}) + public ApiRest paperDetail(@RequestBody BaseIdReqDTO reqDTO) { + //根据ID删除 + ExamDetailRespDTO respDTO = baseService.paperDetail(reqDTO.getId()); + return super.success(respDTO); + } + + /** + * 批量删除 + * @param reqDTO + * @return + */ + @ApiOperation(value = "试题详情") + @RequestMapping(value = "/qu-detail", method = { RequestMethod.POST}) + public ApiRest quDetail(@RequestBody PaperQuQueryDTO reqDTO) { + //根据ID删除 + PaperQuDetailDTO respDTO = baseService.findQuDetail(reqDTO.getPaperId(), reqDTO.getQuId()); + return super.success(respDTO); + } + + /** + * 填充答案 + * @param reqDTO + * @return + */ + @ApiOperation(value = "填充答案") + @RequestMapping(value = "/fill-answer", method = { RequestMethod.POST}) + public ApiRest fillAnswer(@RequestBody PaperAnswerDTO reqDTO) { + //根据ID删除 + baseService.fillAnswer(reqDTO); + return super.success(); + } + + + /** + * 交卷操作 + * @param reqDTO + * @return + */ + @ApiOperation(value = "交卷操作") + @RequestMapping(value = "/hand-exam", method = { RequestMethod.POST}) + public ApiRest handleExam(@RequestBody BaseIdReqDTO reqDTO) { + //根据ID删除 + baseService.handExam(reqDTO.getId()); + return super.success(); + } + + + /** + * 批量删除 + * @param reqDTO + * @return + */ + @ApiOperation(value = "试卷详情") + @RequestMapping(value = "/paper-result", method = { RequestMethod.POST}) + public ApiRest paperResult(@RequestBody BaseIdReqDTO reqDTO) { + //根据ID删除 + ExamResultRespDTO respDTO = baseService.paperResult(reqDTO.getId()); + return super.success(respDTO); + } + + + /** + * 检测用户有没有中断的考试 + * @return + */ + @ApiOperation(value = "检测进行中的考试") + @RequestMapping(value = "/check-process", method = { RequestMethod.POST}) + public ApiRest checkProcess() { + //复制参数 + PaperDTO dto = baseService.checkProcess(UserUtils.getUserId()); + return super.success(dto); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/PaperDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/PaperDTO.java new file mode 100644 index 0000000..dcfb9ef --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/PaperDTO.java @@ -0,0 +1,79 @@ +package com.yf.exam.modules.paper.dto; + +import com.yf.exam.core.annon.Dict; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +*

+* 试卷请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 17:31 +*/ +@Data +@ApiModel(value="试卷", description="试卷") +public class PaperDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "试卷ID", required=true) + private String id; + + @Dict(dictTable = "sys_user", dicText = "real_name", dicCode = "id") + @ApiModelProperty(value = "用户ID", required=true) + private String userId; + + @Dict(dictTable = "sys_depart", dicText = "dept_name", dicCode = "id") + @ApiModelProperty(value = "部门ID", required=true) + private String departId; + + @ApiModelProperty(value = "规则ID", required=true) + private String examId; + + @ApiModelProperty(value = "考试标题", required=true) + private String title; + + @ApiModelProperty(value = "考试时长", required=true) + private Integer totalTime; + + @ApiModelProperty(value = "用户时长", required=true) + private Integer userTime; + + @ApiModelProperty(value = "试卷总分", required=true) + private Integer totalScore; + + @ApiModelProperty(value = "及格分", required=true) + private Integer qualifyScore; + + @ApiModelProperty(value = "客观分", required=true) + private Integer objScore; + + @ApiModelProperty(value = "主观分", required=true) + private Integer subjScore; + + @ApiModelProperty(value = "用户得分", required=true) + private Integer userScore; + + @ApiModelProperty(value = "是否包含简答题", required=true) + private Boolean hasSaq; + + @ApiModelProperty(value = "试卷状态", required=true) + private Integer state; + + @ApiModelProperty(value = "创建时间", required=true) + private Date createTime; + + @ApiModelProperty(value = "更新时间", required=true) + private Date updateTime; + + @ApiModelProperty(value = "截止时间") + private Date limitTime; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/PaperQuAnswerDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/PaperQuAnswerDTO.java new file mode 100644 index 0000000..e449e79 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/PaperQuAnswerDTO.java @@ -0,0 +1,48 @@ +package com.yf.exam.modules.paper.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 试卷考题备选答案请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 17:31 +*/ +@Data +@ApiModel(value="试卷考题备选答案", description="试卷考题备选答案") +public class PaperQuAnswerDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "自增ID", required=true) + private String id; + + @ApiModelProperty(value = "试卷ID", required=true) + private String paperId; + + @ApiModelProperty(value = "回答项ID", required=true) + private String answerId; + + @ApiModelProperty(value = "题目ID", required=true) + private String quId; + + @ApiModelProperty(value = "是否正确项", required=true) + private Boolean isRight; + + @ApiModelProperty(value = "是否选中", required=true) + private Boolean checked; + + @ApiModelProperty(value = "排序", required=true) + private Integer sort; + + @ApiModelProperty(value = "选项标签", required=true) + private String abc; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/PaperQuDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/PaperQuDTO.java new file mode 100644 index 0000000..349868e --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/PaperQuDTO.java @@ -0,0 +1,54 @@ +package com.yf.exam.modules.paper.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 试卷考题请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 17:31 +*/ +@Data +@ApiModel(value="试卷考题", description="试卷考题") +public class PaperQuDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "试卷ID", required=true) + private String paperId; + + @ApiModelProperty(value = "题目ID", required=true) + private String quId; + + @ApiModelProperty(value = "题目类型", required=true) + private Integer quType; + + @ApiModelProperty(value = "是否已答", required=true) + private Boolean answered; + + @ApiModelProperty(value = "主观答案", required=true) + private String answer; + + @ApiModelProperty(value = "问题排序", required=true) + private Integer sort; + + @ApiModelProperty(value = "单题分分值", required=true) + private Integer score; + + @ApiModelProperty(value = "实际得分(主观题)", required=true) + private Integer actualScore; + + @ApiModelProperty(value = "是否答对", required=true) + private Boolean isRight; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuAnswerExtDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuAnswerExtDTO.java new file mode 100644 index 0000000..3124d9e --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuAnswerExtDTO.java @@ -0,0 +1,29 @@ +package com.yf.exam.modules.paper.dto.ext; + +import com.yf.exam.modules.paper.dto.PaperQuAnswerDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** +*

+* 试卷考题备选答案请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 17:31 +*/ +@Data +@ApiModel(value="试卷考题备选答案", description="试卷考题备选答案") +public class PaperQuAnswerExtDTO extends PaperQuAnswerDTO { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "试题图片", required=true) + private String image; + + @ApiModelProperty(value = "答案内容", required=true) + private String content; + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuDetailDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuDetailDTO.java new file mode 100644 index 0000000..ee1e998 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/ext/PaperQuDetailDTO.java @@ -0,0 +1,32 @@ +package com.yf.exam.modules.paper.dto.ext; + +import com.yf.exam.modules.paper.dto.PaperQuDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** +*

+* 试卷考题请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 17:31 +*/ +@Data +@ApiModel(value="试卷题目详情类", description="试卷题目详情类") +public class PaperQuDetailDTO extends PaperQuDTO { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "图片", required=true) + private String image; + + @ApiModelProperty(value = "题目内容", required=true) + private String content; + + @ApiModelProperty(value = "答案内容", required=true) + List answerList; +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperAnswerDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperAnswerDTO.java new file mode 100644 index 0000000..ccbe3ce --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperAnswerDTO.java @@ -0,0 +1,22 @@ +package com.yf.exam.modules.paper.dto.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * @author bool + */ +@Data +@ApiModel(value="查找试卷题目详情请求类", description="查找试卷题目详情请求类") +public class PaperAnswerDTO extends PaperQuQueryDTO { + + @ApiModelProperty(value = "回答列表", required=true) + private List answers; + + @ApiModelProperty(value = "主观答案", required=true) + private String answer; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperCreateReqDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperCreateReqDTO.java new file mode 100644 index 0000000..6a8f8c5 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperCreateReqDTO.java @@ -0,0 +1,22 @@ +package com.yf.exam.modules.paper.dto.request; + +import com.yf.exam.core.api.dto.BaseDTO; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author bool + */ +@Data +@ApiModel(value="试卷创建请求类", description="试卷创建请求类") +public class PaperCreateReqDTO extends BaseDTO { + + @JsonIgnore + private String userId; + + @ApiModelProperty(value = "考试ID", required=true) + private String examId; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperListReqDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperListReqDTO.java new file mode 100644 index 0000000..c333241 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperListReqDTO.java @@ -0,0 +1,39 @@ +package com.yf.exam.modules.paper.dto.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 试卷请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 17:31 +*/ +@Data +@ApiModel(value="试卷", description="试卷") +public class PaperListReqDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "用户ID", required=true) + private String userId; + + @ApiModelProperty(value = "部门ID", required=true) + private String departId; + + @ApiModelProperty(value = "规则ID", required=true) + private String examId; + + @ApiModelProperty(value = "用户昵称", required=true) + private String realName; + + @ApiModelProperty(value = "试卷状态", required=true) + private Integer state; + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperQuQueryDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperQuQueryDTO.java new file mode 100644 index 0000000..48184b6 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/request/PaperQuQueryDTO.java @@ -0,0 +1,21 @@ +package com.yf.exam.modules.paper.dto.request; + +import com.yf.exam.core.api.dto.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author bool + */ +@Data +@ApiModel(value="查找试卷题目详情请求类", description="查找试卷题目详情请求类") +public class PaperQuQueryDTO extends BaseDTO { + + @ApiModelProperty(value = "试卷ID", required=true) + private String paperId; + + @ApiModelProperty(value = "题目ID", required=true) + private String quId; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/response/ExamDetailRespDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/response/ExamDetailRespDTO.java new file mode 100644 index 0000000..c307c84 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/response/ExamDetailRespDTO.java @@ -0,0 +1,38 @@ +package com.yf.exam.modules.paper.dto.response; + +import com.yf.exam.modules.paper.dto.PaperDTO; +import com.yf.exam.modules.paper.dto.PaperQuDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Calendar; +import java.util.List; + +@Data +@ApiModel(value="考试详情", description="考试详情") +public class ExamDetailRespDTO extends PaperDTO { + + + @ApiModelProperty(value = "单选题列表", required=true) + private List radioList; + + @ApiModelProperty(value = "多选题列表", required=true) + private List multiList; + + @ApiModelProperty(value = "判断题", required=true) + private List judgeList; + + + @ApiModelProperty(value = "剩余结束秒数", required=true) + public Long getLeftSeconds(){ + + // 结束时间 + Calendar cl = Calendar.getInstance(); + cl.setTime(this.getCreateTime()); + cl.add(Calendar.MINUTE, getTotalTime()); + + return (cl.getTimeInMillis() - System.currentTimeMillis()) / 1000; + } + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/response/ExamResultRespDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/response/ExamResultRespDTO.java new file mode 100644 index 0000000..64af63d --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/response/ExamResultRespDTO.java @@ -0,0 +1,18 @@ +package com.yf.exam.modules.paper.dto.response; + +import com.yf.exam.modules.paper.dto.PaperDTO; +import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +@Data +@ApiModel(value="考试结果展示响应类", description="考试结果展示响应类") +public class ExamResultRespDTO extends PaperDTO { + + @ApiModelProperty(value = "问题列表", required=true) + private List quList; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/dto/response/PaperListRespDTO.java b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/response/PaperListRespDTO.java new file mode 100644 index 0000000..9e003e4 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/dto/response/PaperListRespDTO.java @@ -0,0 +1,26 @@ +package com.yf.exam.modules.paper.dto.response; + +import com.yf.exam.modules.paper.dto.PaperDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** +*

+* 试卷请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 17:31 +*/ +@Data +@ApiModel(value="试卷列表响应类", description="试卷列表响应类") +public class PaperListRespDTO extends PaperDTO { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "人员", required=true) + private String realName; + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/entity/Paper.java b/exam-api/src/main/java/com/yf/exam/modules/paper/entity/Paper.java new file mode 100644 index 0000000..e219471 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/entity/Paper.java @@ -0,0 +1,125 @@ +package com.yf.exam.modules.paper.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +import java.util.Date; + +/** +*

+* 试卷实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 17:31 +*/ +@Data +@TableName("el_paper") +public class Paper extends Model { + + private static final long serialVersionUID = 1L; + + /** + * 试卷ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 用户ID + */ + @TableField("user_id") + private String userId; + + /** + * 部门ID + */ + @TableField("depart_id") + private String departId; + + /** + * 规则ID + */ + @TableField("exam_id") + private String examId; + + /** + * 考试标题 + */ + private String title; + + /** + * 考试时长 + */ + @TableField("total_time") + private Integer totalTime; + + /** + * 用户时长 + */ + @TableField("user_time") + private Integer userTime; + + /** + * 试卷总分 + */ + @TableField("total_score") + private Integer totalScore; + + /** + * 及格分 + */ + @TableField("qualify_score") + private Integer qualifyScore; + + /** + * 客观分 + */ + @TableField("obj_score") + private Integer objScore; + + /** + * 主观分 + */ + @TableField("subj_score") + private Integer subjScore; + + /** + * 用户得分 + */ + @TableField("user_score") + private Integer userScore; + + /** + * 是否包含简答题 + */ + @TableField("has_saq") + private Boolean hasSaq; + + /** + * 试卷状态 + */ + private Integer state; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + + /** + * 截止时间 + */ + @TableField("limit_time") + private Date limitTime; +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/entity/PaperQu.java b/exam-api/src/main/java/com/yf/exam/modules/paper/entity/PaperQu.java new file mode 100644 index 0000000..3723791 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/entity/PaperQu.java @@ -0,0 +1,80 @@ +package com.yf.exam.modules.paper.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +/** +*

+* 试卷考题实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 17:31 +*/ +@Data +@TableName("el_paper_qu") +public class PaperQu extends Model { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 试卷ID + */ + @TableField("paper_id") + private String paperId; + + /** + * 题目ID + */ + @TableField("qu_id") + private String quId; + + /** + * 题目类型 + */ + @TableField("qu_type") + private Integer quType; + + /** + * 是否已答 + */ + private Boolean answered; + + /** + * 主观答案 + */ + private String answer; + + /** + * 问题排序 + */ + private Integer sort; + + /** + * 单题分分值 + */ + private Integer score; + + /** + * 实际得分(主观题) + */ + @TableField("actual_score") + private Integer actualScore; + + /** + * 是否答对 + */ + @TableField("is_right") + private Boolean isRight; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/entity/PaperQuAnswer.java b/exam-api/src/main/java/com/yf/exam/modules/paper/entity/PaperQuAnswer.java new file mode 100644 index 0000000..b3999da --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/entity/PaperQuAnswer.java @@ -0,0 +1,68 @@ +package com.yf.exam.modules.paper.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +/** +*

+* 试卷考题备选答案实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 17:31 +*/ +@Data +@TableName("el_paper_qu_answer") +public class PaperQuAnswer extends Model { + + + /** + * 自增ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 试卷ID + */ + @TableField("paper_id") + private String paperId; + + /** + * 回答项ID + */ + @TableField("answer_id") + private String answerId; + + /** + * 题目ID + */ + @TableField("qu_id") + private String quId; + + /** + * 是否正确项 + */ + @TableField("is_right") + private Boolean isRight; + + /** + * 是否选中 + */ + private Boolean checked; + + /** + * 排序 + */ + private Integer sort; + + /** + * 选项标签 + */ + private String abc; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/enums/ExamState.java b/exam-api/src/main/java/com/yf/exam/modules/paper/enums/ExamState.java new file mode 100644 index 0000000..a62f404 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/enums/ExamState.java @@ -0,0 +1,33 @@ +package com.yf.exam.modules.paper.enums; + + +/** + * 考试状态 + * @author bool + * @date 2019-10-30 13:11 + */ +public interface ExamState { + + + /** + * 考试中 + */ + Integer ENABLE = 0; + + /** + * 待阅卷 + */ + Integer DISABLED = 1; + + /** + * 已完成 + */ + Integer READY_START = 2; + + /** + * 已结束 + */ + Integer OVERDUE = 3; + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/enums/PaperState.java b/exam-api/src/main/java/com/yf/exam/modules/paper/enums/PaperState.java new file mode 100644 index 0000000..ceed269 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/enums/PaperState.java @@ -0,0 +1,33 @@ +package com.yf.exam.modules.paper.enums; + + +/** + * 试卷状态 + * @author bool + * @date 2019-10-30 13:11 + */ +public interface PaperState { + + + /** + * 考试中 + */ + Integer ING = 0; + + /** + * 待阅卷 + */ + Integer WAIT_OPT = 1; + + /** + * 已完成 + */ + Integer FINISHED = 2; + + /** + * 弃考 + */ + Integer BREAK = 3; + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/job/BreakExamJob.java b/exam-api/src/main/java/com/yf/exam/modules/paper/job/BreakExamJob.java new file mode 100644 index 0000000..fb14f53 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/job/BreakExamJob.java @@ -0,0 +1,45 @@ +package com.yf.exam.modules.paper.job; + +import com.yf.exam.ability.job.service.JobService; +import com.yf.exam.modules.paper.service.PaperService; +import lombok.extern.log4j.Log4j2; +import org.quartz.Job; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 超时自动交卷任务 + * @author bool + */ +@Log4j2 +@Component +public class BreakExamJob implements Job { + + @Autowired + private PaperService paperService; + + @Override + public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { + + JobDetail detail = jobExecutionContext.getJobDetail(); + String name = detail.getKey().getName(); + String group = detail.getKey().getGroup(); + String data = String.valueOf(detail.getJobDataMap().get(JobService.TASK_DATA)); + + log.info("++++++++++定时任务:处理到期的交卷"); + log.info("++++++++++jobName:{}", name); + log.info("++++++++++jobGroup:{}", group); + log.info("++++++++++taskData:{}", data); + + + // 强制交卷 + paperService.handExam(data); + + } + + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/mapper/PaperMapper.java b/exam-api/src/main/java/com/yf/exam/modules/paper/mapper/PaperMapper.java new file mode 100644 index 0000000..1a52de5 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/mapper/PaperMapper.java @@ -0,0 +1,39 @@ +package com.yf.exam.modules.paper.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yf.exam.modules.paper.dto.PaperDTO; +import com.yf.exam.modules.paper.dto.request.PaperListReqDTO; +import com.yf.exam.modules.paper.dto.response.PaperListRespDTO; +import com.yf.exam.modules.paper.entity.Paper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +*

+* 试卷Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 16:33 +*/ +public interface PaperMapper extends BaseMapper { + + /** + * 查找试卷分页 + * @param page + * @param query + * @return + */ + IPage paging(Page page, @Param("query") PaperListReqDTO query); + + + /** + * 试卷列表响应类 + * @param query + * @return + */ + List list(@Param("query") PaperDTO query); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/mapper/PaperQuAnswerMapper.java b/exam-api/src/main/java/com/yf/exam/modules/paper/mapper/PaperQuAnswerMapper.java new file mode 100644 index 0000000..e795ba9 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/mapper/PaperQuAnswerMapper.java @@ -0,0 +1,27 @@ +package com.yf.exam.modules.paper.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO; +import com.yf.exam.modules.paper.entity.PaperQuAnswer; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +*

+* 试卷考题备选答案Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 16:33 +*/ +public interface PaperQuAnswerMapper extends BaseMapper { + + /** + * 查找试卷试题答案列表 + * @param paperId + * @param quId + * @return + */ + List list(@Param("paperId") String paperId, @Param("quId") String quId); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/mapper/PaperQuMapper.java b/exam-api/src/main/java/com/yf/exam/modules/paper/mapper/PaperQuMapper.java new file mode 100644 index 0000000..f833a1b --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/mapper/PaperQuMapper.java @@ -0,0 +1,42 @@ +package com.yf.exam.modules.paper.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; +import com.yf.exam.modules.paper.entity.PaperQu; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +*

+* 试卷考题Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 16:33 +*/ +public interface PaperQuMapper extends BaseMapper { + + /** + * 统计客观分 + * @param paperId + * @return + */ + int sumObjective(@Param("paperId") String paperId); + + /** + * 统计主观分 + * @param paperId + * @return + */ + int sumSubjective(@Param("paperId") String paperId); + + /** + * 找出全部试题列表 + * @param paperId + * @return + */ + List listByPaper(@Param("paperId") String paperId); +} + + diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/service/PaperQuAnswerService.java b/exam-api/src/main/java/com/yf/exam/modules/paper/service/PaperQuAnswerService.java new file mode 100644 index 0000000..a1cfffa --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/service/PaperQuAnswerService.java @@ -0,0 +1,44 @@ +package com.yf.exam.modules.paper.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.paper.dto.PaperQuAnswerDTO; +import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO; +import com.yf.exam.modules.paper.entity.PaperQuAnswer; + +import java.util.List; + +/** +*

+* 试卷考题备选答案业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 16:33 +*/ +public interface PaperQuAnswerService extends IService { + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + /** + * 查找试卷试题答案列表 + * @param paperId + * @param quId + * @return + */ + List listForExam(String paperId, String quId); + + /** + * 查找答案列表,用来填充 + * @param paperId + * @param quId + * @return + */ + List listForFill(String paperId, String quId); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/service/PaperQuService.java b/exam-api/src/main/java/com/yf/exam/modules/paper/service/PaperQuService.java new file mode 100644 index 0000000..d0cc2e3 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/service/PaperQuService.java @@ -0,0 +1,70 @@ +package com.yf.exam.modules.paper.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.paper.dto.PaperQuDTO; +import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; +import com.yf.exam.modules.paper.entity.PaperQu; + +import java.util.List; + +/** +*

+* 试卷考题业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 16:33 +*/ +public interface PaperQuService extends IService { + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + /** + * 根据试卷找出题目列表 + * @param paperId + * @return + */ + List listByPaper(String paperId); + + /** + * 查找详情 + * @param paperId + * @param quId + * @return + */ + PaperQu findByKey(String paperId, String quId); + + /** + * 根据组合索引更新 + * @param qu + */ + void updateByKey(PaperQu qu); + + /** + * 统计客观分 + * @param paperId + * @return + */ + int sumObjective(String paperId); + + /** + * 统计主观分 + * @param paperId + * @return + */ + int sumSubjective(String paperId); + + /** + * 找出全部试题列表 + * @param paperId + * @return + */ + List listForPaperResult(String paperId); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/service/PaperService.java b/exam-api/src/main/java/com/yf/exam/modules/paper/service/PaperService.java new file mode 100644 index 0000000..042bb7d --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/service/PaperService.java @@ -0,0 +1,83 @@ +package com.yf.exam.modules.paper.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.paper.dto.PaperDTO; +import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; +import com.yf.exam.modules.paper.dto.request.PaperAnswerDTO; +import com.yf.exam.modules.paper.dto.request.PaperListReqDTO; +import com.yf.exam.modules.paper.dto.response.ExamDetailRespDTO; +import com.yf.exam.modules.paper.dto.response.ExamResultRespDTO; +import com.yf.exam.modules.paper.dto.response.PaperListRespDTO; +import com.yf.exam.modules.paper.entity.Paper; + +/** +*

+* 试卷业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 16:33 +*/ +public interface PaperService extends IService { + + /** + * 创建试卷 + * @param userId + * @param examId + * @return + */ + String createPaper(String userId, String examId); + + + /** + * 查找详情 + * @param paperId + * @return + */ + ExamDetailRespDTO paperDetail(String paperId); + + /** + * 考试结果 + * @param paperId + * @return + */ + ExamResultRespDTO paperResult(String paperId); + + /** + * 查找题目详情 + * @param paperId + * @param quId + * @return + */ + PaperQuDetailDTO findQuDetail(String paperId, String quId); + + /** + * 填充答案 + * @param reqDTO + */ + void fillAnswer(PaperAnswerDTO reqDTO); + + /** + * 交卷操作 + * @param paperId + * @return + */ + void handExam(String paperId); + + /** + * 试卷列表响应类 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + /** + * 检测是否有进行中的考试 + * @param userId + * @return + */ + PaperDTO checkProcess(String userId); + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/service/impl/PaperQuAnswerServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/paper/service/impl/PaperQuAnswerServiceImpl.java new file mode 100644 index 0000000..c2dc0b7 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/service/impl/PaperQuAnswerServiceImpl.java @@ -0,0 +1,61 @@ +package com.yf.exam.modules.paper.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.paper.dto.PaperQuAnswerDTO; +import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO; +import com.yf.exam.modules.paper.entity.PaperQuAnswer; +import com.yf.exam.modules.paper.mapper.PaperQuAnswerMapper; +import com.yf.exam.modules.paper.service.PaperQuAnswerService; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** +*

+* 语言设置 服务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 16:33 +*/ +@Service +public class PaperQuAnswerServiceImpl extends ServiceImpl implements PaperQuAnswerService { + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + //创建分页对象 + IPage query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); + + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + + //获得数据 + IPage page = this.page(query, wrapper); + //转换结果 + IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); + return pageData; + } + + @Override + public List listForExam(String paperId, String quId) { + return baseMapper.list(paperId, quId); + } + + @Override + public List listForFill(String paperId, String quId) { + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .eq(PaperQuAnswer::getPaperId, paperId) + .eq(PaperQuAnswer::getQuId, quId); + + return this.list(wrapper); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/service/impl/PaperQuServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/paper/service/impl/PaperQuServiceImpl.java new file mode 100644 index 0000000..a0d9a1d --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/service/impl/PaperQuServiceImpl.java @@ -0,0 +1,94 @@ +package com.yf.exam.modules.paper.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.paper.dto.PaperQuDTO; +import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; +import com.yf.exam.modules.paper.entity.PaperQu; +import com.yf.exam.modules.paper.mapper.PaperQuMapper; +import com.yf.exam.modules.paper.service.PaperQuService; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** +*

+* 语言设置 服务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 16:33 +*/ +@Service +public class PaperQuServiceImpl extends ServiceImpl implements PaperQuService { + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + //创建分页对象 + IPage query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); + + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + + //获得数据 + IPage page = this.page(query, wrapper); + //转换结果 + IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); + return pageData; + } + + @Override + public List listByPaper(String paperId) { + + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(PaperQu::getPaperId, paperId) + .orderByAsc(PaperQu::getSort); + + List list = this.list(wrapper); + return BeanMapper.mapList(list, PaperQuDTO.class); + } + + @Override + public PaperQu findByKey(String paperId, String quId) { + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(PaperQu::getPaperId, paperId) + .eq(PaperQu::getQuId, quId); + + return this.getOne(wrapper, false); + } + + @Override + public void updateByKey(PaperQu qu) { + + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(PaperQu::getPaperId, qu.getPaperId()) + .eq(PaperQu::getQuId, qu.getQuId()); + + this.update(qu, wrapper); + } + + @Override + public int sumObjective(String paperId) { + return baseMapper.sumObjective(paperId); + } + + @Override + public int sumSubjective(String paperId) { + return baseMapper.sumSubjective(paperId); + } + + @Override + public List listForPaperResult(String paperId) { + return baseMapper.listByPaper(paperId); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/paper/service/impl/PaperServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/paper/service/impl/PaperServiceImpl.java new file mode 100644 index 0000000..2eefec8 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/paper/service/impl/PaperServiceImpl.java @@ -0,0 +1,542 @@ +package com.yf.exam.modules.paper.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.ability.job.enums.JobGroup; +import com.yf.exam.ability.job.enums.JobPrefix; +import com.yf.exam.ability.job.service.JobService; +import com.yf.exam.core.api.ApiError; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.exception.ServiceException; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.core.utils.CronUtils; +import com.yf.exam.modules.exam.dto.ExamDTO; +import com.yf.exam.modules.exam.dto.ExamRepoDTO; +import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO; +import com.yf.exam.modules.exam.service.ExamRepoService; +import com.yf.exam.modules.exam.service.ExamService; +import com.yf.exam.modules.paper.dto.PaperDTO; +import com.yf.exam.modules.paper.dto.PaperQuDTO; +import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO; +import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO; +import com.yf.exam.modules.paper.dto.request.PaperAnswerDTO; +import com.yf.exam.modules.paper.dto.request.PaperListReqDTO; +import com.yf.exam.modules.paper.dto.response.ExamDetailRespDTO; +import com.yf.exam.modules.paper.dto.response.ExamResultRespDTO; +import com.yf.exam.modules.paper.dto.response.PaperListRespDTO; +import com.yf.exam.modules.paper.entity.Paper; +import com.yf.exam.modules.paper.entity.PaperQu; +import com.yf.exam.modules.paper.entity.PaperQuAnswer; +import com.yf.exam.modules.paper.enums.ExamState; +import com.yf.exam.modules.paper.enums.PaperState; +import com.yf.exam.modules.paper.job.BreakExamJob; +import com.yf.exam.modules.paper.mapper.PaperMapper; +import com.yf.exam.modules.paper.service.PaperQuAnswerService; +import com.yf.exam.modules.paper.service.PaperQuService; +import com.yf.exam.modules.paper.service.PaperService; +import com.yf.exam.modules.qu.entity.Qu; +import com.yf.exam.modules.qu.entity.QuAnswer; +import com.yf.exam.modules.qu.enums.QuType; +import com.yf.exam.modules.qu.service.QuAnswerService; +import com.yf.exam.modules.qu.service.QuService; +import com.yf.exam.modules.sys.user.entity.SysUser; +import com.yf.exam.modules.sys.user.service.SysUserService; +import com.yf.exam.modules.user.book.service.UserBookService; +import com.yf.exam.modules.user.exam.service.UserExamService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.util.*; + +/** +*

+* 语言设置 服务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 16:33 +*/ +@Service +public class PaperServiceImpl extends ServiceImpl implements PaperService { + + + @Autowired + private SysUserService sysUserService; + + @Autowired + private ExamService examService; + + @Autowired + private QuService quService; + + @Autowired + private QuAnswerService quAnswerService; + + @Autowired + private PaperService paperService; + + @Autowired + private PaperQuService paperQuService; + + @Autowired + private PaperQuAnswerService paperQuAnswerService; + + @Autowired + private UserBookService userBookService; + + @Autowired + private ExamRepoService examRepoService; + + @Autowired + private UserExamService userExamService; + + @Autowired + private JobService jobService; + + /** + * 展示的选项,ABC这样 + */ + private static List ABC = Arrays.asList(new String[]{ + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K","L","M","N","O","P","Q","R","S","T","U","V","W","X" + ,"Y","Z" + }); + + + + + + @Transactional(rollbackFor = Exception.class) + @Override + public String createPaper(String userId, String examId) { + + // 校验是否有正在考试的试卷 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .eq(Paper::getUserId, userId) + .eq(Paper::getState, PaperState.ING); + + int exists = this.count(wrapper); + + + if (exists > 0) { + throw new ServiceException(ApiError.ERROR_20010002); + } + + // 查找考试 + ExamDTO exam = examService.findById(examId); + + if(exam == null){ + throw new ServiceException(1, "考试不存在!"); + } + + if(!ExamState.ENABLE.equals(exam.getState())){ + throw new ServiceException(1, "考试状态不正确!"); + } + + // 考试题目列表 + List quList = this.generateByRepo(examId); + + if(CollectionUtils.isEmpty(quList)){ + throw new ServiceException(1, "规则不正确,无对应的考题!"); + } + + //保存试卷内容 + Paper paper = this.savePaper(userId, exam, quList); + + // 强制交卷任务 + String jobName = JobPrefix.BREAK_EXAM + paper.getId(); + jobService.addCronJob(BreakExamJob.class, jobName, CronUtils.dateToCron(paper.getLimitTime()), paper.getId()); + + return paper.getId(); + } + + @Override + public ExamDetailRespDTO paperDetail(String paperId) { + + + ExamDetailRespDTO respDTO = new ExamDetailRespDTO(); + + // 试题基本信息 + Paper paper = paperService.getById(paperId); + BeanMapper.copy(paper, respDTO); + + // 查找题目列表 + List list = paperQuService.listByPaper(paperId); + + List radioList = new ArrayList<>(); + List multiList = new ArrayList<>(); + List judgeList = new ArrayList<>(); + for(PaperQuDTO item: list){ + if(QuType.RADIO.equals(item.getQuType())){ + radioList.add(item); + } + if(QuType.MULTI.equals(item.getQuType())){ + multiList.add(item); + } + if(QuType.JUDGE.equals(item.getQuType())){ + judgeList.add(item); + } + } + + respDTO.setRadioList(radioList); + respDTO.setMultiList(multiList); + respDTO.setJudgeList(judgeList); + return respDTO; + } + + @Override + public ExamResultRespDTO paperResult(String paperId) { + + ExamResultRespDTO respDTO = new ExamResultRespDTO(); + + // 试题基本信息 + Paper paper = paperService.getById(paperId); + BeanMapper.copy(paper, respDTO); + + List quList = paperQuService.listForPaperResult(paperId); + respDTO.setQuList(quList); + + return respDTO; + } + + @Override + public PaperQuDetailDTO findQuDetail(String paperId, String quId) { + + PaperQuDetailDTO respDTO = new PaperQuDetailDTO(); + // 问题 + Qu qu = quService.getById(quId); + + // 基本信息 + PaperQu paperQu = paperQuService.findByKey(paperId, quId); + BeanMapper.copy(paperQu, respDTO); + respDTO.setContent(qu.getContent()); + respDTO.setImage(qu.getImage()); + + // 答案列表 + List list = paperQuAnswerService.listForExam(paperId, quId); + respDTO.setAnswerList(list); + + return respDTO; + } + + + /** + * 题库组题方式产生题目列表 + * @param examId + * @return + */ + private List generateByRepo(String examId){ + + // 查找规则指定的题库 + List list = examRepoService.listByExam(examId); + + //最终的题目列表 + List quList = new ArrayList<>(); + + //排除ID,避免题目重复 + List excludes = new ArrayList<>(); + excludes.add("none"); + + if (!CollectionUtils.isEmpty(list)) { + for (ExamRepoExtDTO item : list) { + + // 单选题 + if(item.getRadioCount() > 0){ + List radioList = quService.listByRandom(item.getRepoId(), QuType.RADIO, excludes, item.getRadioCount()); + for (Qu qu : radioList) { + PaperQu paperQu = this.processPaperQu(item, qu); + quList.add(paperQu); + excludes.add(qu.getId()); + } + } + + //多选题 + if(item.getMultiCount() > 0) { + List multiList = quService.listByRandom(item.getRepoId(), QuType.MULTI, excludes, + item.getMultiCount()); + for (Qu qu : multiList) { + PaperQu paperQu = this.processPaperQu(item, qu); + quList.add(paperQu); + excludes.add(qu.getId()); + } + } + + // 判断题 + if(item.getJudgeCount() > 0) { + List judgeList = quService.listByRandom(item.getRepoId(), QuType.JUDGE, excludes, + item.getJudgeCount()); + for (Qu qu : judgeList) { + PaperQu paperQu = this.processPaperQu(item, qu); + quList.add(paperQu); + excludes.add(qu.getId()); + } + } + } + } + return quList; + } + + + + /** + * 填充试题题目信息 + * @param repo + * @param qu + * @return + */ + private PaperQu processPaperQu(ExamRepoDTO repo, Qu qu) { + + //保存试题信息 + PaperQu paperQu = new PaperQu(); + paperQu.setQuId(qu.getId()); + paperQu.setAnswered(false); + paperQu.setIsRight(false); + paperQu.setQuType(qu.getQuType()); + + if (QuType.RADIO.equals(qu.getQuType())) { + paperQu.setScore(repo.getRadioScore()); + paperQu.setActualScore(repo.getRadioScore()); + } + + if (QuType.MULTI.equals(qu.getQuType())) { + paperQu.setScore(repo.getMultiScore()); + paperQu.setActualScore(repo.getMultiScore()); + } + + if (QuType.JUDGE.equals(qu.getQuType())) { + paperQu.setScore(repo.getJudgeScore()); + paperQu.setActualScore(repo.getJudgeScore()); + } + + return paperQu; + } + + + /** + * 保存试卷 + * @param userId + * @param exam + * @param quList + * @return + */ + private Paper savePaper(String userId, ExamDTO exam, List quList) { + + + // 查找用户 + SysUser user = sysUserService.getById(userId); + + //保存试卷基本信息 + Paper paper = new Paper(); + paper.setDepartId(user.getDepartId()); + paper.setExamId(exam.getId()); + paper.setTitle(exam.getTitle()); + paper.setTotalScore(exam.getTotalScore()); + paper.setTotalTime(exam.getTotalTime()); + paper.setUserScore(0); + paper.setUserId(userId); + paper.setCreateTime(new Date()); + paper.setUpdateTime(new Date()); + paper.setQualifyScore(exam.getQualifyScore()); + paper.setState(PaperState.ING); + paper.setHasSaq(false); + + // 截止时间 + Calendar cl = Calendar.getInstance(); + cl.setTimeInMillis(System.currentTimeMillis()); + cl.add(Calendar.MINUTE, exam.getTotalTime()); + paper.setLimitTime(cl.getTime()); + + paperService.save(paper); + + if (!CollectionUtils.isEmpty(quList)) { + this.savePaperQu(paper.getId(), quList); + } + + return paper; + } + + + + /** + * 保存试卷试题列表 + * @param paperId + * @param quList + */ + private void savePaperQu(String paperId, List quList){ + + List batchQuList = new ArrayList<>(); + List batchAnswerList = new ArrayList<>(); + + int sort = 0; + for (PaperQu item : quList) { + + item.setPaperId(paperId); + item.setSort(sort); + item.setId(IdWorker.getIdStr()); + + //回答列表 + List answerList = quAnswerService.listAnswerByRandom(item.getQuId()); + + if (!CollectionUtils.isEmpty(answerList)) { + + int ii = 0; + for (QuAnswer answer : answerList) { + PaperQuAnswer paperQuAnswer = new PaperQuAnswer(); + paperQuAnswer.setId(UUID.randomUUID().toString()); + paperQuAnswer.setPaperId(paperId); + paperQuAnswer.setQuId(answer.getQuId()); + paperQuAnswer.setAnswerId(answer.getId()); + paperQuAnswer.setChecked(false); + paperQuAnswer.setSort(ii); + paperQuAnswer.setAbc(ABC.get(ii)); + paperQuAnswer.setIsRight(answer.getIsRight()); + ii++; + batchAnswerList.add(paperQuAnswer); + } + } + + batchQuList.add(item); + sort++; + } + + //添加问题 + paperQuService.saveBatch(batchQuList); + + //批量添加问题答案 + paperQuAnswerService.saveBatch(batchAnswerList); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void fillAnswer(PaperAnswerDTO reqDTO) { + + + // 未作答 + if(CollectionUtils.isEmpty(reqDTO.getAnswers()) + && StringUtils.isBlank(reqDTO.getAnswer())){ + return; + } + + //查找答案列表 + List list = paperQuAnswerService.listForFill(reqDTO.getPaperId(), reqDTO.getQuId()); + + //是否正确 + boolean right = true; + + //更新正确答案 + for (PaperQuAnswer item : list) { + + if (reqDTO.getAnswers().contains(item.getId())) { + item.setChecked(true); + } else { + item.setChecked(false); + } + + //有一个对不上就是错的 + if (item.getIsRight()!=null && !item.getIsRight().equals(item.getChecked())) { + right = false; + } + paperQuAnswerService.updateById(item); + } + + //修改为已回答 + PaperQu qu = new PaperQu(); + qu.setQuId(reqDTO.getQuId()); + qu.setPaperId(reqDTO.getPaperId()); + qu.setIsRight(right); + qu.setAnswer(reqDTO.getAnswer()); + qu.setAnswered(true); + + paperQuService.updateByKey(qu); + + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void handExam(String paperId) { + + //获取试卷信息 + Paper paper = paperService.getById(paperId); + + //如果不是正常的,抛出异常 + if(!PaperState.ING.equals(paper.getState())){ + throw new ServiceException(1, "试卷状态不正确!"); + } + + // 客观分 + int objScore = paperQuService.sumObjective(paperId); + paper.setObjScore(objScore); + paper.setUserScore(objScore); + + // 主观分,因为要阅卷,所以给0 + paper.setSubjScore(0); + + // 待阅卷 + if(paper.getHasSaq()) { + paper.setState(PaperState.WAIT_OPT); + }else { + + // 同步保存考试成绩 + userExamService.joinResult(paper.getUserId(), paper.getExamId(), objScore, objScore>=paper.getQualifyScore()); + + paper.setState(PaperState.FINISHED); + } + paper.setUpdateTime(new Date()); + + //计算考试时长 + Calendar cl = Calendar.getInstance(); + cl.setTimeInMillis(System.currentTimeMillis()); + int userTime = (int)((System.currentTimeMillis() - paper.getCreateTime().getTime()) / 1000 / 60); + if(userTime == 0){ + userTime = 1; + } + paper.setUserTime(userTime); + + //更新试卷 + paperService.updateById(paper); + + + // 终止定时任务 + String name = JobPrefix.BREAK_EXAM + paperId; + jobService.deleteJob(name, JobGroup.SYSTEM); + + //把打错的问题加入错题本 + List list = paperQuService.listByPaper(paperId); + for(PaperQuDTO qu: list){ + // 主观题和对的都不加入错题库 + if(qu.getIsRight()){ + continue; + } + //加入错题本 + new Thread(() -> userBookService.addBook(paper.getExamId(), qu.getQuId())).run(); + } + } + + @Override + public IPage paging(PagingReqDTO reqDTO) { + return baseMapper.paging(reqDTO.toPage(), reqDTO.getParams()); + } + + + @Override + public PaperDTO checkProcess(String userId) { + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .eq(Paper::getUserId, userId) + .eq(Paper::getState, PaperState.ING); + + Paper paper = this.getOne(wrapper, false); + + if (paper != null) { + return BeanMapper.map(paper, PaperDTO.class); + } + + return null; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/controller/QuController.java b/exam-api/src/main/java/com/yf/exam/modules/qu/controller/QuController.java new file mode 100644 index 0000000..07e7d76 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/controller/QuController.java @@ -0,0 +1,324 @@ +package com.yf.exam.modules.qu.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.google.common.collect.Lists; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import com.yf.exam.core.api.dto.BaseIdReqDTO; +import com.yf.exam.core.api.dto.BaseIdRespDTO; +import com.yf.exam.core.api.dto.BaseIdsReqDTO; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.exception.ServiceException; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.core.utils.excel.ExportExcel; +import com.yf.exam.core.utils.excel.ImportExcel; +import com.yf.exam.modules.qu.dto.QuDTO; +import com.yf.exam.modules.qu.dto.export.QuExportDTO; +import com.yf.exam.modules.qu.dto.ext.QuDetailDTO; +import com.yf.exam.modules.qu.dto.request.QuQueryReqDTO; +import com.yf.exam.modules.qu.entity.Qu; +import com.yf.exam.modules.qu.service.QuService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.CollectionUtils; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +/** +*

+* 问题题目控制器 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:25 +*/ +@Api(tags={"问题题目"}) +@RestController +@RequestMapping("/exam/api/qu/qu") +public class QuController extends BaseController { + + @Autowired + private QuService baseService; + + /** + * 添加或修改 + * + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "添加或修改") + @RequestMapping(value = "/save", method = {RequestMethod.POST}) + public ApiRest save(@RequestBody QuDetailDTO reqDTO) { + baseService.save(reqDTO); + return super.success(); + } + + /** + * 批量删除 + * + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "批量删除") + @RequestMapping(value = "/delete", method = {RequestMethod.POST}) + public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) { + //根据ID删除 + baseService.delete(reqDTO.getIds()); + return super.success(); + } + + /** + * 查找详情 + * + * @param reqDTO + * @return + */ + @ApiOperation(value = "查找详情") + @RequestMapping(value = "/detail", method = {RequestMethod.POST}) + public ApiRest detail(@RequestBody BaseIdReqDTO reqDTO) { + QuDetailDTO dto = baseService.detail(reqDTO.getId()); + return super.success(dto); + } + + /** + * 分页查找 + * + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "分页查找") + @RequestMapping(value = "/paging", method = {RequestMethod.POST}) + public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.paging(reqDTO); + + return super.success(page); + } + + + /** + * 导出excel文件 + */ + @RequiresRoles("sa") + @ResponseBody + @RequestMapping(value = "/export") + public ApiRest exportFile(HttpServletResponse response, @RequestBody QuQueryReqDTO reqDTO) { + + + // 导出文件名 + String fileName = "导出的试题-" + System.currentTimeMillis() + ".xlsx"; + + try { + + int no = 0; + String quId = ""; + List list = baseService.listForExport(reqDTO); + for (QuExportDTO item : list) { + if (!quId.equals(item.getQId())) { + quId = item.getQId(); + no += 1; + } else { + item.setQuType("0"); + item.setQContent(""); + item.setQAnalysis(""); + item.setRepoList(null); + item.setQImage(""); + item.setQVideo(""); + } + item.setNo(String.valueOf(no)); + } + new ExportExcel("试题", QuExportDTO.class).setDataList(list).write(response, fileName).dispose(); + return super.success(); + } catch (Exception e) { + return failure(e.getMessage()); + } + } + + /** + * 导入Excel + * + * @param file + * @return + */ + @RequiresRoles("sa") + @ResponseBody + @RequestMapping(value = "/import") + public ApiRest importFile(@RequestParam("file") MultipartFile file) { + + try { + + ImportExcel ei = new ImportExcel(file, 1, 0); + List list = ei.getDataList(QuExportDTO.class); + + // 校验数据 + this.checkExcel(list); + + // 导入数据条数 + baseService.importExcel(list); + + // 导入成功 + return super.success(); + + } catch (IOException e) { + + } catch (InvalidFormatException e) { + + } catch (IllegalAccessException e) { + + } catch (InstantiationException e) { + + } + + return super.failure(); + } + + /** + * 校验Excel + * + * @param list + * @throws Exception + */ + private void checkExcel(List list) throws ServiceException { + + // 约定第三行开始导入 + int line = 3; + StringBuffer sb = new StringBuffer(); + + if (CollectionUtils.isEmpty(list)) { + throw new ServiceException(1, "您导入的数据似乎是一个空表格!"); + } + + Integer quNo = null; + for (QuExportDTO item : list) { + + System.out.println(item.getNo()); + if (StringUtils.isBlank(item.getNo())) { + line++; + continue; + } + + System.out.println(item.getQContent()); + Integer no; + + try { + no = Integer.parseInt(item.getNo()); + } catch (Exception e) { + line++; + continue; + } + + if (no == null) { + sb.append("第" + line + "行,题目序号不能为空!
"); + } + + if (quNo == null || !quNo.equals(no)) { + + if (item.getQuType() == null) { + sb.append("第" + line + "行,题目类型不能为空
"); + } + + if (StringUtils.isBlank(item.getQContent())) { + sb.append("第" + line + "行,题目内容不能为空
"); + } + + if (CollectionUtils.isEmpty(item.getRepoList())) { + sb.append("第" + line + "行,题目必须包含一个题库
"); + } + } + + if (StringUtils.isBlank(item.getAIsRight())) { + sb.append("第" + line + "行,选项是否正确不能为空
"); + } + + if (StringUtils.isBlank(item.getAContent()) && StringUtils.isBlank(item.getAImage())) { + sb.append("第" + line + "行,选项内容和选项图片必须有一个不为空
"); + } + + quNo = no; + line++; + } + + // 存在错误 + if (!"".equals(sb.toString())) { + throw new ServiceException(1, sb.toString()); + } + } + + /** + * 下载导入试题数据模板 + */ + @ResponseBody + @RequestMapping(value = "import/template") + public ApiRest importFileTemplate(HttpServletResponse response) { + try { + String fileName = "试题导入模板.xlsx"; + List list = Lists.newArrayList(); + + QuExportDTO l1 = new QuExportDTO(); + l1.setNo("正式导入,请删除此说明行:数字,相同的数字表示同一题的序列"); + l1.setQContent("问题内容"); + l1.setQAnalysis("整个问题的解析"); + l1.setQuType("只能填写1、2、3、4;1表示单选题,2表示多选题,3表示判断题,4表示主观题"); + l1.setQImage("题目图片,完整URL,多个用逗号隔开,限制10个"); + l1.setQVideo("题目视频,完整URL,只限一个"); + l1.setAImage("答案图片,完整URL,只限一个"); + l1.setRepoList(Arrays.asList(new String[]{"已存在题库的ID,多个用逗号隔开,题库ID错误无法导入"})); + l1.setAContent("候选答案1"); + l1.setAIsRight("只能填写0或1,0表示否,1表示是"); + l1.setAAnalysis("这个项是正确的"); + + + QuExportDTO l2 = new QuExportDTO(); + l2.setQContent("找出以下可以被2整除的数(多选)"); + l2.setQAnalysis("最基本的数学题,不做过多解析"); + l2.setQuType("2"); + l2.setNo("1"); + l2.setAIsRight("1"); + l2.setAContent("数字:2"); + l2.setAAnalysis("2除以2=1,对的"); + + QuExportDTO l3 = new QuExportDTO(); + l3.setNo("1"); + l3.setAIsRight("0"); + l3.setAContent("数字:3"); + l3.setAAnalysis("3除以2=1.5,不能被整除"); + + QuExportDTO l4 = new QuExportDTO(); + l4.setNo("1"); + l4.setAIsRight("1"); + l4.setAContent("数字:6"); + l4.setAAnalysis("6除以2=3,对的"); + + + + list.add(l1); + list.add(l2); + list.add(l3); + list.add(l4); + + new ExportExcel("试题数据", QuExportDTO.class, 1).setDataList(list).write(response, fileName).dispose(); + return super.success(); + } catch (Exception e) { + return super.failure("导入模板下载失败!失败信息:"+e.getMessage()); + } + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/dto/QuAnswerDTO.java b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/QuAnswerDTO.java new file mode 100644 index 0000000..3b9c3a1 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/QuAnswerDTO.java @@ -0,0 +1,42 @@ +package com.yf.exam.modules.qu.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 候选答案请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@ApiModel(value="候选答案", description="候选答案") +public class QuAnswerDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "答案ID", required=true) + private String id; + + @ApiModelProperty(value = "问题ID", required=true) + private String quId; + + @ApiModelProperty(value = "是否正确", required=true) + private Boolean isRight; + + @ApiModelProperty(value = "选项图片", required=true) + private String image; + + @ApiModelProperty(value = "答案内容", required=true) + private String content; + + @ApiModelProperty(value = "答案分析", required=true) + private String analysis; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/dto/QuDTO.java b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/QuDTO.java new file mode 100644 index 0000000..8f45eec --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/QuDTO.java @@ -0,0 +1,53 @@ +package com.yf.exam.modules.qu.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +*

+* 问题题目请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@ApiModel(value="问题题目", description="问题题目") +public class QuDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "题目ID", required=true) + private String id; + + @ApiModelProperty(value = "题目类型", required=true) + private Integer quType; + + @ApiModelProperty(value = "1普通,2较难", required=true) + private Integer level; + + @ApiModelProperty(value = "题目图片", required=true) + private String image; + + @ApiModelProperty(value = "题目内容", required=true) + private String content; + + + @ApiModelProperty(value = "创建时间", required=true) + private Date createTime; + + @ApiModelProperty(value = "更新时间", required=true) + private Date updateTime; + + @ApiModelProperty(value = "题目备注", required=true) + private String remark; + + @ApiModelProperty(value = "整题解析", required=true) + private String analysis; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/dto/QuRepoDTO.java b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/QuRepoDTO.java new file mode 100644 index 0000000..6e689b8 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/QuRepoDTO.java @@ -0,0 +1,38 @@ +package com.yf.exam.modules.qu.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 试题题库请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@ApiModel(value="试题题库", description="试题题库") +public class QuRepoDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + private String id; + + @ApiModelProperty(value = "试题", required=true) + private String quId; + + @ApiModelProperty(value = "归属题库", required=true) + private String repoId; + + @ApiModelProperty(value = "题目类型", required=true) + private Integer quType; + + @ApiModelProperty(value = "排序", required=true) + private Integer sort; + +} \ No newline at end of file diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/dto/export/QuExportDTO.java b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/export/QuExportDTO.java new file mode 100644 index 0000000..fe94dac --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/export/QuExportDTO.java @@ -0,0 +1,45 @@ +package com.yf.exam.modules.qu.dto.export; + +import com.yf.exam.core.utils.excel.annotation.ExcelField; +import com.yf.exam.core.utils.excel.fieldtype.ListType; +import lombok.Data; + +import java.util.List; + +/** + * 用于导出的数据结构 + * @author bool + */ +@Data +public class QuExportDTO { + + private static final long serialVersionUID = 1L; + + /** + * + */ + private String qId; + + @ExcelField(title="题目序号", align=2, sort=1) + private String no; + @ExcelField(title="题目类型", align=2, sort=2) + private String quType; + @ExcelField(title="题目内容", align=2, sort=3) + private String qContent; + @ExcelField(title="整体解析", align=2, sort=4) + private String qAnalysis; + @ExcelField(title="题目图片", align=2, sort=5) + private String qImage; + @ExcelField(title="题目视频", align=2, sort=6) + private String qVideo; + @ExcelField(title="所属题库", align=2, sort=7, fieldType = ListType.class) + private List repoList; + @ExcelField(title="是否正确项", align=2, sort=8) + private String aIsRight; + @ExcelField(title="选项内容", align=2, sort=9) + private String aContent; + @ExcelField(title="选项解析", align=2, sort=10) + private String aAnalysis; + @ExcelField(title="选项图片", align=2, sort=11) + private String aImage; +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/dto/export/QuImportDTO.java b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/export/QuImportDTO.java new file mode 100644 index 0000000..4ac03c0 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/export/QuImportDTO.java @@ -0,0 +1,23 @@ +package com.yf.exam.modules.qu.dto.export; + +import com.yf.exam.modules.qu.dto.QuAnswerDTO; +import lombok.Data; + +import java.util.List; + +/** + * 用于导出的数据结构 + * @author bool + */ +@Data +public class QuImportDTO { + + private static final long serialVersionUID = 1L; + + private String quType; + private String qContent; + private String qAnalysis; + private String qImage; + private String repoName; + private List answerList; +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/dto/ext/QuDetailDTO.java b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/ext/QuDetailDTO.java new file mode 100644 index 0000000..b36a4e8 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/ext/QuDetailDTO.java @@ -0,0 +1,33 @@ +package com.yf.exam.modules.qu.dto.ext; + +import com.yf.exam.modules.qu.dto.QuAnswerDTO; +import com.yf.exam.modules.qu.dto.QuDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** +*

+* 问题题目请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@ApiModel(value="问题题目详情", description="问题题目详情") +public class QuDetailDTO extends QuDTO { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "备选项列表", required=true) + private List answerList; + + @ApiModelProperty(value = "题库列表", required=true) + private List repoIds; + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/dto/request/QuQueryReqDTO.java b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/request/QuQueryReqDTO.java new file mode 100644 index 0000000..a927e9e --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/request/QuQueryReqDTO.java @@ -0,0 +1,38 @@ +package com.yf.exam.modules.qu.dto.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** +*

+* 问题题目请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@ApiModel(value="题目查询请求类", description="题目查询请求类") +public class QuQueryReqDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "题目类型") + private Integer quType; + + @ApiModelProperty(value = "归属题库") + private List repoIds; + + @ApiModelProperty(value = "题目内容") + private String content; + + @ApiModelProperty(value = "排除ID列表") + private List excludes; + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/dto/request/QuRepoBatchReqDTO.java b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/request/QuRepoBatchReqDTO.java new file mode 100644 index 0000000..3ca148d --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/dto/request/QuRepoBatchReqDTO.java @@ -0,0 +1,34 @@ +package com.yf.exam.modules.qu.dto.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** +*

+* 问题题目请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@ApiModel(value="试题题库批量操作类", description="试题题库批量操作类") +public class QuRepoBatchReqDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "题目ID", required=true) + private List quIds; + + @ApiModelProperty(value = "题目类型", required=true) + private List repoIds; + + @ApiModelProperty(value = "是否移除,否就新增;是就移除", required=true) + private Boolean remove; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/entity/Qu.java b/exam-api/src/main/java/com/yf/exam/modules/qu/entity/Qu.java new file mode 100644 index 0000000..2380cdc --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/entity/Qu.java @@ -0,0 +1,75 @@ +package com.yf.exam.modules.qu.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +import java.util.Date; + +/** +*

+* 问题题目实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@TableName("el_qu") +public class Qu extends Model { + + private static final long serialVersionUID = 1L; + + /** + * 题目ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 题目类型 + */ + @TableField("qu_type") + private Integer quType; + + /** + * 1普通,2较难 + */ + private Integer level; + + /** + * 题目图片 + */ + private String image; + + /** + * 题目内容 + */ + private String content; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + + /** + * 题目备注 + */ + private String remark; + + /** + * 整题解析 + */ + private String analysis; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/entity/QuAnswer.java b/exam-api/src/main/java/com/yf/exam/modules/qu/entity/QuAnswer.java new file mode 100644 index 0000000..45e9bf4 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/entity/QuAnswer.java @@ -0,0 +1,58 @@ +package com.yf.exam.modules.qu.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +/** +*

+* 候选答案实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@TableName("el_qu_answer") +public class QuAnswer extends Model { + + private static final long serialVersionUID = 1L; + + /** + * 答案ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 问题ID + */ + @TableField("qu_id") + private String quId; + + /** + * 是否正确 + */ + @TableField("is_right") + private Boolean isRight; + + /** + * 选项图片 + */ + private String image; + + /** + * 答案内容 + */ + private String content; + + + /** + * 答案分析 + */ + private String analysis; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/entity/QuRepo.java b/exam-api/src/main/java/com/yf/exam/modules/qu/entity/QuRepo.java new file mode 100644 index 0000000..baade9a --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/entity/QuRepo.java @@ -0,0 +1,50 @@ +package com.yf.exam.modules.qu.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +/** +*

+* 试题题库实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@TableName("el_qu_repo") +public class QuRepo extends Model { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 试题 + */ + @TableField("qu_id") + private String quId; + + /** + * 归属题库 + */ + @TableField("repo_id") + private String repoId; + + /** + * 题目类型 + */ + @TableField("qu_type") + private Integer quType; + + /** + * 排序 + */ + private Integer sort; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/enums/QuType.java b/exam-api/src/main/java/com/yf/exam/modules/qu/enums/QuType.java new file mode 100644 index 0000000..70e2ee1 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/enums/QuType.java @@ -0,0 +1,26 @@ +package com.yf.exam.modules.qu.enums; + + +/** + * 题目类型 + * @author bool + * @date 2019-10-30 13:11 + */ +public interface QuType { + + /** + * 单选题 + */ + Integer RADIO = 1; + + /** + * 多选题 + */ + Integer MULTI = 2; + + /** + * 判断题 + */ + Integer JUDGE = 3; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/mapper/QuAnswerMapper.java b/exam-api/src/main/java/com/yf/exam/modules/qu/mapper/QuAnswerMapper.java new file mode 100644 index 0000000..23699ce --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/mapper/QuAnswerMapper.java @@ -0,0 +1,16 @@ +package com.yf.exam.modules.qu.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.qu.entity.QuAnswer; + +/** +*

+* 候选答案Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +public interface QuAnswerMapper extends BaseMapper { + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/mapper/QuMapper.java b/exam-api/src/main/java/com/yf/exam/modules/qu/mapper/QuMapper.java new file mode 100644 index 0000000..14069a2 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/mapper/QuMapper.java @@ -0,0 +1,56 @@ +package com.yf.exam.modules.qu.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yf.exam.modules.qu.dto.QuDTO; +import com.yf.exam.modules.qu.dto.export.QuExportDTO; +import com.yf.exam.modules.qu.dto.request.QuQueryReqDTO; +import com.yf.exam.modules.qu.entity.Qu; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +*

+* 问题题目Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +public interface QuMapper extends BaseMapper { + + + + /** + * 随机抽取题库的数据 + * @param repoId + * @param quType + * @param level + * @param excludes 要排除的ID列表 + * @param size + * @return + */ + List listByRandom(@Param("repoId") String repoId, + @Param("quType") Integer quType, + @Param("excludes") List excludes, + @Param("size") Integer size); + + /** + * 查找导出列表 + * @param query + * @return + */ + List listForExport(@Param("query") QuQueryReqDTO query); + + /** + * 分页查找 + * @param page + * @param query + * @return + */ + IPage paging(Page page, @Param("query") QuQueryReqDTO query); + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/mapper/QuRepoMapper.java b/exam-api/src/main/java/com/yf/exam/modules/qu/mapper/QuRepoMapper.java new file mode 100644 index 0000000..1015448 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/mapper/QuRepoMapper.java @@ -0,0 +1,16 @@ +package com.yf.exam.modules.qu.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.qu.entity.QuRepo; + +/** +*

+* 试题题库Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +public interface QuRepoMapper extends BaseMapper { + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/service/QuAnswerService.java b/exam-api/src/main/java/com/yf/exam/modules/qu/service/QuAnswerService.java new file mode 100644 index 0000000..4062e95 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/service/QuAnswerService.java @@ -0,0 +1,48 @@ +package com.yf.exam.modules.qu.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.qu.dto.QuAnswerDTO; +import com.yf.exam.modules.qu.entity.QuAnswer; + +import java.util.List; + +/** +*

+* 候选答案业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +public interface QuAnswerService extends IService { + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + /** + * 根据题目ID查询答案并随机 + * @param quId + * @return + */ + List listAnswerByRandom(String quId); + + /** + * 根据问题查找答案 + * @param quId + * @return + */ + List listByQu(String quId); + + /** + * 保存试题 + * @param quId + * @param list + */ + void saveAll(String quId, List list); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/service/QuRepoService.java b/exam-api/src/main/java/com/yf/exam/modules/qu/service/QuRepoService.java new file mode 100644 index 0000000..f528759 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/service/QuRepoService.java @@ -0,0 +1,59 @@ +package com.yf.exam.modules.qu.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.qu.dto.QuRepoDTO; +import com.yf.exam.modules.qu.dto.request.QuRepoBatchReqDTO; +import com.yf.exam.modules.qu.entity.QuRepo; + +import java.util.List; + +/** +*

+* 试题题库业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +public interface QuRepoService extends IService { + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + /** + * 保存全部列表 + * @param quId + * @param quType + * @param ids + */ + void saveAll(String quId, Integer quType, List ids); + + /** + * 根据问题查找题库 + * @param quId + * @return + */ + List listByQu(String quId); + + /** + * 根据题库查找题目ID列表 + * @param repoId + * @param quType + * @param rand + * @return + */ + List listByRepo(String repoId, Integer quType, boolean rand); + + /** + * 批量操作 + * @param reqDTO + */ + void batchAction(QuRepoBatchReqDTO reqDTO); + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/service/QuService.java b/exam-api/src/main/java/com/yf/exam/modules/qu/service/QuService.java new file mode 100644 index 0000000..81f43b0 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/service/QuService.java @@ -0,0 +1,76 @@ +package com.yf.exam.modules.qu.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.qu.dto.QuDTO; +import com.yf.exam.modules.qu.dto.export.QuExportDTO; +import com.yf.exam.modules.qu.dto.ext.QuDetailDTO; +import com.yf.exam.modules.qu.dto.request.QuQueryReqDTO; +import com.yf.exam.modules.qu.entity.Qu; + +import java.util.List; + +/** +*

+* 问题题目业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +public interface QuService extends IService { + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + /** + * 删除试题 + * @param ids + */ + void delete(List ids); + + /** + * 随机抽取题库的数据 + * @param repoId + * @param quType + * @param excludes 要排除的ID列表 + * @param size + * @return + */ + List listByRandom(String repoId, + Integer quType, + List excludes, + Integer size); + + /** + * 问题详情 + * @param id + * @return + */ + QuDetailDTO detail(String id); + + /** + * 保存试题 + * @param reqDTO + */ + void save(QuDetailDTO reqDTO); + + /** + * 查找导出列表 + * @param query + * @return + */ + List listForExport(QuQueryReqDTO query); + + /** + * 导入Excel + * @param dtoList + * @return + */ + int importExcel(List dtoList); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/service/impl/QuAnswerServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/qu/service/impl/QuAnswerServiceImpl.java new file mode 100644 index 0000000..2fd66cf --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/service/impl/QuAnswerServiceImpl.java @@ -0,0 +1,144 @@ +package com.yf.exam.modules.qu.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.qu.dto.QuAnswerDTO; +import com.yf.exam.modules.qu.entity.QuAnswer; +import com.yf.exam.modules.qu.mapper.QuAnswerMapper; +import com.yf.exam.modules.qu.service.QuAnswerService; +import com.yf.exam.modules.qu.utils.ImageCheckUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; + +/** +*

+* 语言设置 服务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Service +public class QuAnswerServiceImpl extends ServiceImpl implements QuAnswerService { + + @Autowired + private ImageCheckUtils imageCheckUtils; + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + //创建分页对象 + IPage query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); + + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + + //获得数据 + IPage page = this.page(query, wrapper); + //转换结果 + IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); + return pageData; + } + + @Override + public List listAnswerByRandom(String quId) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(QuAnswer::getQuId, quId); + wrapper.last(" ORDER BY RAND() "); + + return this.list(wrapper); + } + + @Override + public List listByQu(String quId) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(QuAnswer::getQuId, quId); + + List list = this.list(wrapper); + if(!CollectionUtils.isEmpty(list)){ + return BeanMapper.mapList(list, QuAnswerDTO.class); + } + + return null; + } + + + /** + * 查找已存在的列表 + * @param quId + * @return + */ + public List findExistsList(String quId) { + //返回结果 + List ids = new ArrayList<>(); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.lambda().eq(QuAnswer::getQuId, quId); + List list = this.list(wrapper); + + if (!CollectionUtils.isEmpty(list)) { + for (QuAnswer item : list) { + ids.add(item.getId()); + } + } + return ids; + } + + @Override + public void saveAll(String quId, List list) { + + //最终要保存的列表 + List saveList = new ArrayList<>(); + + //已存在的标签列表 + List ids = this.findExistsList(quId); + + if(!CollectionUtils.isEmpty(list)){ + for(QuAnswerDTO item: list){ + + // 校验图片地址 + imageCheckUtils.checkImage(item.getImage(), "选项图片地址错误!"); + + //标签ID + String id = item.getId(); + QuAnswer answer = new QuAnswer(); + BeanMapper.copy(item, answer); + answer.setQuId(quId); + + //补全ID避免新增 + if(ids.contains(id)){ + ids.remove(id); + } + + saveList.add(answer); + } + + //保存标签列表 + if(!CollectionUtils.isEmpty(saveList)) { + this.saveOrUpdateBatch(saveList); + } + + //删除已移除 + if(!ids.isEmpty()){ + this.removeByIds(ids); + } + }else{ + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(QuAnswer::getQuId, quId); + this.remove(wrapper); + } + } + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/service/impl/QuRepoServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/qu/service/impl/QuRepoServiceImpl.java new file mode 100644 index 0000000..b7de030 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/service/impl/QuRepoServiceImpl.java @@ -0,0 +1,175 @@ +package com.yf.exam.modules.qu.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.qu.dto.QuRepoDTO; +import com.yf.exam.modules.qu.dto.request.QuRepoBatchReqDTO; +import com.yf.exam.modules.qu.entity.Qu; +import com.yf.exam.modules.qu.entity.QuRepo; +import com.yf.exam.modules.qu.mapper.QuMapper; +import com.yf.exam.modules.qu.mapper.QuRepoMapper; +import com.yf.exam.modules.qu.service.QuRepoService; +import com.yf.exam.modules.repo.service.RepoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; + +/** +*

+* 语言设置 服务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Service +public class QuRepoServiceImpl extends ServiceImpl implements QuRepoService { + + + @Autowired + private QuMapper quMapper; + + @Autowired + private RepoService repoService; + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + //创建分页对象 + IPage query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); + + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + + //获得数据 + IPage page = this.page(query, wrapper); + //转换结果 + IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); + return pageData; + } + + @Override + public void saveAll(String quId, Integer quType, List ids) { + // 先删除 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(QuRepo::getQuId, quId); + this.remove(wrapper); + + // 保存全部 + if(!CollectionUtils.isEmpty(ids)){ + List list = new ArrayList<>(); + for(String id: ids){ + QuRepo ref = new QuRepo(); + ref.setQuId(quId); + ref.setRepoId(id); + ref.setQuType(quType); + list.add(ref); + } + this.saveBatch(list); + + + for(String id: ids){ + this.sortRepo(id); + } + } + + + } + + @Override + public List listByQu(String quId) { + // 先删除 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(QuRepo::getQuId, quId); + List list = this.list(wrapper); + List ids = new ArrayList<>(); + if(!CollectionUtils.isEmpty(list)){ + for(QuRepo item: list){ + ids.add(item.getRepoId()); + } + } + return ids; + } + + @Override + public List listByRepo(String repoId, Integer quType, boolean rand) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .eq(QuRepo::getRepoId, repoId); + + if(quType!=null){ + wrapper.lambda().eq(QuRepo::getQuType, quType); + } + + if(rand){ + wrapper.orderByAsc(" RAND() "); + }else{ + wrapper.lambda().orderByAsc(QuRepo::getSort); + } + + List list = this.list(wrapper); + List ids = new ArrayList<>(); + if(!CollectionUtils.isEmpty(list)){ + for(QuRepo item: list){ + ids.add(item.getQuId()); + } + } + return ids; + } + + @Override + public void batchAction(QuRepoBatchReqDTO reqDTO) { + + // 移除的 + if(reqDTO.getRemove()!=null && reqDTO.getRemove()){ + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .in(QuRepo::getRepoId, reqDTO.getRepoIds()) + .in(QuRepo::getQuId, reqDTO.getQuIds()); + this.remove(wrapper); + }else{ + + // 新增的 + for(String quId : reqDTO.getQuIds()){ + Qu q = quMapper.selectById(quId); + this.saveAll(quId, q.getQuType(), reqDTO.getRepoIds()); + } + } + + for(String id: reqDTO.getRepoIds()){ + this.sortRepo(id); + } + + } + + + /** + * 单个题库进行排序 + * @param repoId + */ + private void sortRepo(String repoId){ + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(QuRepo::getRepoId, repoId); + + List list = this.list(wrapper); + if(CollectionUtils.isEmpty(list)){ + return; + } + + int sort = 1; + for(QuRepo item: list){ + item.setSort(sort); + sort++; + } + this.updateBatchById(list); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/service/impl/QuServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/qu/service/impl/QuServiceImpl.java new file mode 100644 index 0000000..f9ae656 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/service/impl/QuServiceImpl.java @@ -0,0 +1,283 @@ +package com.yf.exam.modules.qu.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.ability.upload.config.UploadConfig; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.exception.ServiceException; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.qu.dto.QuAnswerDTO; +import com.yf.exam.modules.qu.dto.QuDTO; +import com.yf.exam.modules.qu.dto.export.QuExportDTO; +import com.yf.exam.modules.qu.dto.ext.QuDetailDTO; +import com.yf.exam.modules.qu.dto.request.QuQueryReqDTO; +import com.yf.exam.modules.qu.entity.Qu; +import com.yf.exam.modules.qu.entity.QuAnswer; +import com.yf.exam.modules.qu.entity.QuRepo; +import com.yf.exam.modules.qu.enums.QuType; +import com.yf.exam.modules.qu.mapper.QuMapper; +import com.yf.exam.modules.qu.service.QuAnswerService; +import com.yf.exam.modules.qu.service.QuRepoService; +import com.yf.exam.modules.qu.service.QuService; +import com.yf.exam.modules.qu.utils.ImageCheckUtils; +import com.yf.exam.modules.repo.service.RepoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

+ * 语言设置 服务实现类 + *

+ * + * @author 聪明笨狗 + * @since 2020-05-25 10:17 + */ +@Service +public class QuServiceImpl extends ServiceImpl implements QuService { + + @Autowired + private QuAnswerService quAnswerService; + + @Autowired + private QuRepoService quRepoService; + + @Autowired + private ImageCheckUtils imageCheckUtils; + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + //创建分页对象 + Page page = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); + + //转换结果 + IPage pageData = baseMapper.paging(page, reqDTO.getParams()); + return pageData; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void delete(List ids) { + // 移除题目 + this.removeByIds(ids); + + // 移除选项 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().in(QuAnswer::getQuId, ids); + quAnswerService.remove(wrapper); + + // 移除题库绑定 + QueryWrapper wrapper1 = new QueryWrapper<>(); + wrapper1.lambda().in(QuRepo::getQuId, ids); + quRepoService.remove(wrapper1); + } + + @Override + public List listByRandom(String repoId, Integer quType, List excludes, Integer size) { + return baseMapper.listByRandom(repoId, quType, excludes, size); + } + + @Override + public QuDetailDTO detail(String id) { + + QuDetailDTO respDTO = new QuDetailDTO(); + Qu qu = this.getById(id); + BeanMapper.copy(qu, respDTO); + + List answerList = quAnswerService.listByQu(id); + respDTO.setAnswerList(answerList); + + List repoIds = quRepoService.listByQu(id); + respDTO.setRepoIds(repoIds); + + return respDTO; + } + + + @Transactional(rollbackFor = Exception.class) + @Override + public void save(QuDetailDTO reqDTO) { + + + // 校验数据 + this.checkData(reqDTO, ""); + + Qu qu = new Qu(); + BeanMapper.copy(reqDTO, qu); + + // 校验图片地址 + imageCheckUtils.checkImage(qu.getImage(), "题干图片地址错误!"); + + // 更新 + this.saveOrUpdate(qu); + + // 保存全部问题 + quAnswerService.saveAll(qu.getId(), reqDTO.getAnswerList()); + + // 保存到题库 + quRepoService.saveAll(qu.getId(), qu.getQuType(), reqDTO.getRepoIds()); + + } + + @Override + public List listForExport(QuQueryReqDTO query) { + return baseMapper.listForExport(query); + } + + @Override + public int importExcel(List dtoList) { + + //根据题目名称分组 + Map> anMap = new HashMap<>(16); + + //题目本体信息 + Map quMap = new HashMap<>(16); + + //数据分组 + for (QuExportDTO item : dtoList) { + + // 空白的ID + if (StringUtils.isEmpty(item.getNo())) { + continue; + } + + Integer key; + //序号 + try { + key = Integer.parseInt(item.getNo()); + } catch (Exception e) { + continue; + } + + //如果已经有题目了,直接处理选项 + if (anMap.containsKey(key)) { + anMap.get(key).add(item); + } else { + //如果没有,将题目内容和选项一起 + List subList = new ArrayList<>(); + subList.add(item); + anMap.put(key, subList); + quMap.put(key, item); + } + } + + int count = 0; + try { + + //循环题目插入 + for (Integer key : quMap.keySet()) { + + QuExportDTO im = quMap.get(key); + + //题目基本信息 + QuDetailDTO qu = new QuDetailDTO(); + qu.setContent(im.getQContent()); + qu.setAnalysis(im.getQAnalysis()); + qu.setQuType(Integer.parseInt(im.getQuType())); + qu.setCreateTime(new Date()); + + //设置回答列表 + List answerList = this.processAnswerList(anMap.get(key)); + //设置题目 + qu.setAnswerList(answerList); + //设置引用题库 + qu.setRepoIds(im.getRepoList()); + // 保存答案 + this.save(qu); + count++; + } + + } catch (ServiceException e) { + e.printStackTrace(); + throw new ServiceException(1, "导入出现问题,行:" + count + "," + e.getMessage()); + } + + return count; + } + + /** + * 处理回答列表 + * + * @param importList + * @return + */ + private List processAnswerList(List importList) { + + List list = new ArrayList<>(16); + for (QuExportDTO item : importList) { + QuAnswerDTO a = new QuAnswerDTO(); + a.setIsRight("1".equals(item.getAIsRight())); + a.setContent(item.getAContent()); + a.setAnalysis(item.getAAnalysis()); + a.setId(""); + list.add(a); + } + return list; + } + + /** + * 校验题目信息 + * + * @param qu + * @param no + * @throws Exception + */ + public void checkData(QuDetailDTO qu, String no) { + + + if (StringUtils.isEmpty(qu.getContent())) { + throw new ServiceException(1, no + "题目内容不能为空!"); + } + + + if (CollectionUtils.isEmpty(qu.getRepoIds())) { + throw new ServiceException(1, no + "至少要选择一个题库!"); + } + + List answers = qu.getAnswerList(); + + + if (CollectionUtils.isEmpty(answers)) { + throw new ServiceException(1, no + "客观题至少要包含一个备选答案!"); + } + + + int trueCount = 0; + for (QuAnswerDTO a : answers) { + + if (a.getIsRight() == null) { + throw new ServiceException(1, no + "必须定义选项是否正确项!"); + } + + if (StringUtils.isEmpty(a.getContent())) { + throw new ServiceException(1, no + "选项内容不为空!"); + } + + if (a.getIsRight()) { + trueCount += 1; + } + } + + if (trueCount == 0) { + throw new ServiceException(1, no + "至少要包含一个正确项!"); + } + + + //单选题 + if (qu.getQuType().equals(QuType.RADIO) && trueCount > 1) { + throw new ServiceException(1, no + "单选题不能包含多个正确项!"); + } + + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/qu/utils/ImageCheckUtils.java b/exam-api/src/main/java/com/yf/exam/modules/qu/utils/ImageCheckUtils.java new file mode 100644 index 0000000..707fbcd --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/qu/utils/ImageCheckUtils.java @@ -0,0 +1,31 @@ +package com.yf.exam.modules.qu.utils; + +import com.yf.exam.ability.upload.config.UploadConfig; +import com.yf.exam.core.exception.ServiceException; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ImageCheckUtils { + + @Autowired + private UploadConfig conf; + + /** + * 进行图片校验! + * @param image + * @param throwMsg + */ + public void checkImage(String image, String throwMsg) { + + if(StringUtils.isBlank(image)){ + return; + } + + // 校验图片地址 + if(!image.startsWith(conf.getUrl())){ + throw new ServiceException(throwMsg); + } + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/repo/controller/RepoController.java b/exam-api/src/main/java/com/yf/exam/modules/repo/controller/RepoController.java new file mode 100644 index 0000000..6b1764f --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/repo/controller/RepoController.java @@ -0,0 +1,117 @@ +package com.yf.exam.modules.repo.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import com.yf.exam.core.api.dto.BaseIdReqDTO; +import com.yf.exam.core.api.dto.BaseIdsReqDTO; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.qu.dto.request.QuRepoBatchReqDTO; +import com.yf.exam.modules.qu.service.QuRepoService; +import com.yf.exam.modules.repo.dto.RepoDTO; +import com.yf.exam.modules.repo.dto.request.RepoReqDTO; +import com.yf.exam.modules.repo.dto.response.RepoRespDTO; +import com.yf.exam.modules.repo.entity.Repo; +import com.yf.exam.modules.repo.service.RepoService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** +*

+* 题库控制器 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:25 +*/ +@Api(tags={"题库"}) +@RestController +@RequestMapping("/exam/api/repo") +public class RepoController extends BaseController { + + @Autowired + private RepoService baseService; + + @Autowired + private QuRepoService quRepoService; + + /** + * 添加或修改 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "添加或修改") + @RequestMapping(value = "/save", method = { RequestMethod.POST}) + public ApiRest save(@RequestBody RepoDTO reqDTO) { + baseService.save(reqDTO); + return super.success(); + } + + /** + * 批量删除 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "批量删除") + @RequestMapping(value = "/delete", method = { RequestMethod.POST}) + public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) { + //根据ID删除 + baseService.removeByIds(reqDTO.getIds()); + return super.success(); + } + + /** + * 查找详情 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "查找详情") + @RequestMapping(value = "/detail", method = { RequestMethod.POST}) + public ApiRest find(@RequestBody BaseIdReqDTO reqDTO) { + Repo entity = baseService.getById(reqDTO.getId()); + RepoDTO dto = new RepoDTO(); + BeanUtils.copyProperties(entity, dto); + return super.success(dto); + } + + /** + * 分页查找 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "分页查找") + @RequestMapping(value = "/paging", method = { RequestMethod.POST}) + public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.paging(reqDTO); + + return super.success(page); + } + + /** + * 批量操作 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "批量操作", notes = "批量加入或从题库移除") + @RequestMapping(value = "/batch-action", method = { RequestMethod.POST}) + public ApiRest batchAction(@RequestBody QuRepoBatchReqDTO reqDTO) { + + //分页查询并转换 + quRepoService.batchAction(reqDTO); + return super.success(); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/repo/dto/RepoDTO.java b/exam-api/src/main/java/com/yf/exam/modules/repo/dto/RepoDTO.java new file mode 100644 index 0000000..a048c6b --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/repo/dto/RepoDTO.java @@ -0,0 +1,43 @@ +package com.yf.exam.modules.repo.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +*

+* 题库请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@ApiModel(value="题库", description="题库") +public class RepoDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "题库ID", required=true) + private String id; + + @ApiModelProperty(value = "题库编号", required=true) + private String code; + + @ApiModelProperty(value = "题库名称", required=true) + private String title; + + @ApiModelProperty(value = "题库备注", required=true) + private String remark; + + @ApiModelProperty(value = "创建时间", required=true) + private Date createTime; + + @ApiModelProperty(value = "更新时间", required=true) + private Date updateTime; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/repo/dto/request/RepoReqDTO.java b/exam-api/src/main/java/com/yf/exam/modules/repo/dto/request/RepoReqDTO.java new file mode 100644 index 0000000..1e4d803 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/repo/dto/request/RepoReqDTO.java @@ -0,0 +1,30 @@ +package com.yf.exam.modules.repo.dto.request; + +import com.yf.exam.modules.repo.dto.RepoDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** +*

+* 题库请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@ApiModel(value="题库分页请求类", description="题库分页请求类") +public class RepoReqDTO extends RepoDTO { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "排除题库ID", required=true) + private List excludes; + + @ApiModelProperty(value = "单选题数量", required=true) + private String title; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/repo/dto/response/RepoRespDTO.java b/exam-api/src/main/java/com/yf/exam/modules/repo/dto/response/RepoRespDTO.java new file mode 100644 index 0000000..feb90aa --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/repo/dto/response/RepoRespDTO.java @@ -0,0 +1,31 @@ +package com.yf.exam.modules.repo.dto.response; + +import com.yf.exam.modules.repo.dto.RepoDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** +*

+* 题库请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@ApiModel(value="题库分页响应类", description="题库分页响应类") +public class RepoRespDTO extends RepoDTO { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "多选题数量", required=true) + private Integer multiCount; + + @ApiModelProperty(value = "单选题数量", required=true) + private Integer radioCount; + + @ApiModelProperty(value = "判断题数量", required=true) + private Integer judgeCount; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/repo/entity/Repo.java b/exam-api/src/main/java/com/yf/exam/modules/repo/entity/Repo.java new file mode 100644 index 0000000..95d3d04 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/repo/entity/Repo.java @@ -0,0 +1,59 @@ +package com.yf.exam.modules.repo.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +import java.util.Date; + +/** +*

+* 题库实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Data +@TableName("el_repo") +public class Repo extends Model { + + private static final long serialVersionUID = 1L; + + /** + * 题库ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 题库编号 + */ + private String code; + + /** + * 题库名称 + */ + private String title; + + /** + * 题库备注 + */ + private String remark; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/repo/mapper/RepoMapper.java b/exam-api/src/main/java/com/yf/exam/modules/repo/mapper/RepoMapper.java new file mode 100644 index 0000000..af39177 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/repo/mapper/RepoMapper.java @@ -0,0 +1,29 @@ +package com.yf.exam.modules.repo.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yf.exam.modules.repo.dto.request.RepoReqDTO; +import com.yf.exam.modules.repo.dto.response.RepoRespDTO; +import com.yf.exam.modules.repo.entity.Repo; +import org.apache.ibatis.annotations.Param; + +/** +*

+* 题库Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +public interface RepoMapper extends BaseMapper { + + /** + * 分页查询题库 + * @param page + * @param query + * @return + */ + IPage paging(Page page, @Param("query") RepoReqDTO query); + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/repo/service/RepoService.java b/exam-api/src/main/java/com/yf/exam/modules/repo/service/RepoService.java new file mode 100644 index 0000000..867a283 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/repo/service/RepoService.java @@ -0,0 +1,34 @@ +package com.yf.exam.modules.repo.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.repo.dto.RepoDTO; +import com.yf.exam.modules.repo.dto.request.RepoReqDTO; +import com.yf.exam.modules.repo.dto.response.RepoRespDTO; +import com.yf.exam.modules.repo.entity.Repo; + +/** +*

+* 题库业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +public interface RepoService extends IService { + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + + /** + * 保存 + * @param reqDTO + */ + void save(RepoDTO reqDTO); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/repo/service/impl/RepoServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/repo/service/impl/RepoServiceImpl.java new file mode 100644 index 0000000..df67301 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/repo/service/impl/RepoServiceImpl.java @@ -0,0 +1,39 @@ +package com.yf.exam.modules.repo.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.repo.dto.RepoDTO; +import com.yf.exam.modules.repo.dto.request.RepoReqDTO; +import com.yf.exam.modules.repo.dto.response.RepoRespDTO; +import com.yf.exam.modules.repo.entity.Repo; +import com.yf.exam.modules.repo.mapper.RepoMapper; +import com.yf.exam.modules.repo.service.RepoService; +import org.springframework.stereotype.Service; + +/** +*

+* 语言设置 服务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-25 13:23 +*/ +@Service +public class RepoServiceImpl extends ServiceImpl implements RepoService { + + @Override + public IPage paging(PagingReqDTO reqDTO) { + return baseMapper.paging(reqDTO.toPage(), reqDTO.getParams()); + } + + @Override + public void save(RepoDTO reqDTO) { + + //复制参数 + Repo entity = new Repo(); + BeanMapper.copy(reqDTO, entity); + this.saveOrUpdate(entity); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/config/controller/SysConfigController.java b/exam-api/src/main/java/com/yf/exam/modules/sys/config/controller/SysConfigController.java new file mode 100644 index 0000000..b62603b --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/config/controller/SysConfigController.java @@ -0,0 +1,70 @@ +package com.yf.exam.modules.sys.config.controller; + +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import com.yf.exam.core.api.dto.BaseIdRespDTO; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.qu.utils.ImageCheckUtils; +import com.yf.exam.modules.sys.config.dto.SysConfigDTO; +import com.yf.exam.modules.sys.config.entity.SysConfig; +import com.yf.exam.modules.sys.config.service.SysConfigService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** +*

+* 通用配置控制器 +*

+* +* @author 聪明笨狗 +* @since 2020-04-17 09:12 +*/ +@Api(tags={"通用配置"}) +@RestController +@RequestMapping("/exam/api/sys/config") +public class SysConfigController extends BaseController { + + @Autowired + private SysConfigService baseService; + + @Autowired + private ImageCheckUtils imageCheckUtils; + + /** + * 添加或修改 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "添加或修改") + @RequestMapping(value = "/save", method = { RequestMethod.POST}) + public ApiRest save(@RequestBody SysConfigDTO reqDTO) { + + //复制参数 + SysConfig entity = new SysConfig(); + BeanMapper.copy(reqDTO, entity); + + // 校验图片地址 + imageCheckUtils.checkImage(entity.getBackLogo(), "系统LOGO地址错误!"); + + baseService.saveOrUpdate(entity); + return super.success(new BaseIdRespDTO(entity.getId())); + } + + /** + * 查找详情 + * @return + */ + @ApiOperation(value = "查找详情") + @RequestMapping(value = "/detail", method = { RequestMethod.POST}) + public ApiRest find() { + SysConfigDTO dto = baseService.find(); + return super.success(dto); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/config/dto/SysConfigDTO.java b/exam-api/src/main/java/com/yf/exam/modules/sys/config/dto/SysConfigDTO.java new file mode 100644 index 0000000..ef7c179 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/config/dto/SysConfigDTO.java @@ -0,0 +1,39 @@ +package com.yf.exam.modules.sys.config.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 通用配置请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-17 09:12 +*/ +@Data +@ApiModel(value="通用配置", description="通用配置") +public class SysConfigDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "系统名称") + private String siteName; + + @ApiModelProperty(value = "前端LOGO") + private String frontLogo; + + @ApiModelProperty(value = "后台LOGO") + private String backLogo; + + @ApiModelProperty(value = "版权信息") + private String copyRight; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/config/entity/SysConfig.java b/exam-api/src/main/java/com/yf/exam/modules/sys/config/entity/SysConfig.java new file mode 100644 index 0000000..070ab06 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/config/entity/SysConfig.java @@ -0,0 +1,53 @@ +package com.yf.exam.modules.sys.config.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +/** +*

+* 通用配置实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-17 09:12 +*/ +@Data +@TableName("sys_config") +public class SysConfig extends Model { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 系统名称 + */ + @TableField("site_name") + private String siteName; + + /** + * 前端LOGO + */ + @TableField("front_logo") + private String frontLogo; + + /** + * 后台LOGO + */ + @TableField("back_logo") + private String backLogo; + + /** + * 版权信息 + */ + @TableField("copy_right") + private String copyRight; +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/config/mapper/SysConfigMapper.java b/exam-api/src/main/java/com/yf/exam/modules/sys/config/mapper/SysConfigMapper.java new file mode 100644 index 0000000..512ae1b --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/config/mapper/SysConfigMapper.java @@ -0,0 +1,16 @@ +package com.yf.exam.modules.sys.config.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.sys.config.entity.SysConfig; + +/** +*

+* 通用配置Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-04-17 09:12 +*/ +public interface SysConfigMapper extends BaseMapper { + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/config/service/SysConfigService.java b/exam-api/src/main/java/com/yf/exam/modules/sys/config/service/SysConfigService.java new file mode 100644 index 0000000..224b09d --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/config/service/SysConfigService.java @@ -0,0 +1,22 @@ +package com.yf.exam.modules.sys.config.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.modules.sys.config.dto.SysConfigDTO; +import com.yf.exam.modules.sys.config.entity.SysConfig; + +/** +*

+* 通用配置业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-17 09:12 +*/ +public interface SysConfigService extends IService { + + /** + * 查找配置信息 + * @return + */ + SysConfigDTO find(); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/config/service/impl/SysConfigServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/sys/config/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..d91880c --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/config/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,34 @@ +package com.yf.exam.modules.sys.config.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.sys.config.dto.SysConfigDTO; +import com.yf.exam.modules.sys.config.entity.SysConfig; +import com.yf.exam.modules.sys.config.mapper.SysConfigMapper; +import com.yf.exam.modules.sys.config.service.SysConfigService; +import org.springframework.stereotype.Service; + +/** +*

+* 语言设置 服务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-17 09:12 +*/ +@Service +public class SysConfigServiceImpl extends ServiceImpl implements SysConfigService { + + @Override + public SysConfigDTO find() { + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.last(" LIMIT 1"); + + SysConfig entity = this.getOne(wrapper, false); + SysConfigDTO dto = new SysConfigDTO(); + BeanMapper.copy(entity, dto); + return dto; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/depart/controller/SysDepartController.java b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/controller/SysDepartController.java new file mode 100644 index 0000000..486fa9a --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/controller/SysDepartController.java @@ -0,0 +1,150 @@ +package com.yf.exam.modules.sys.depart.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import com.yf.exam.core.api.dto.BaseIdReqDTO; +import com.yf.exam.core.api.dto.BaseIdsReqDTO; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.sys.depart.dto.SysDepartDTO; +import com.yf.exam.modules.sys.depart.dto.request.DepartSortReqDTO; +import com.yf.exam.modules.sys.depart.dto.response.SysDepartTreeDTO; +import com.yf.exam.modules.sys.depart.entity.SysDepart; +import com.yf.exam.modules.sys.depart.service.SysDepartService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** +*

+* 部门信息控制器 +*

+* +* @author 聪明笨狗 +* @since 2020-09-02 17:25 +*/ +@Api(tags={"部门信息"}) +@RestController +@RequestMapping("/exam/api/sys/depart") +public class SysDepartController extends BaseController { + + @Autowired + private SysDepartService baseService; + + /** + * 添加或修改 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "添加或修改") + @RequestMapping(value = "/save", method = { RequestMethod.POST}) + public ApiRest save(@RequestBody SysDepartDTO reqDTO) { + baseService.save(reqDTO); + return super.success(); + } + + /** + * 批量删除 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "批量删除") + @RequestMapping(value = "/delete", method = { RequestMethod.POST}) + public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) { + //根据ID删除 + baseService.removeByIds(reqDTO.getIds()); + return super.success(); + } + + /** + * 查找详情 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "查找详情") + @RequestMapping(value = "/detail", method = { RequestMethod.POST}) + public ApiRest find(@RequestBody BaseIdReqDTO reqDTO) { + SysDepart entity = baseService.getById(reqDTO.getId()); + SysDepartDTO dto = new SysDepartDTO(); + BeanUtils.copyProperties(entity, dto); + return super.success(dto); + } + + /** + * 分页查找 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "分页查找") + @RequestMapping(value = "/paging", method = { RequestMethod.POST}) + public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.paging(reqDTO); + + return super.success(page); + } + + /** + * 查找列表,每次最多返回200条数据 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "查找列表") + @RequestMapping(value = "/list", method = { RequestMethod.POST}) + public ApiRest> list(@RequestBody SysDepartDTO reqDTO) { + + //分页查询并转换 + QueryWrapper wrapper = new QueryWrapper<>(); + + //转换并返回 + List list = baseService.list(wrapper); + + //转换数据 + List dtoList = BeanMapper.mapList(list, SysDepartDTO.class); + + return super.success(dtoList); + } + + + /** + * 树列表 + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "树列表") + @RequestMapping(value = "/tree", method = { RequestMethod.POST}) + public ApiRest> tree() { + List dtoList = baseService.findTree(); + return super.success(dtoList); + } + + + /** + * 分类排序 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "分类排序") + @RequestMapping(value = "/sort", method = { RequestMethod.POST}) + public ApiRest sort(@RequestBody DepartSortReqDTO reqDTO) { + baseService.sort(reqDTO.getId(), reqDTO.getSort()); + return super.success(); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/depart/dto/SysDepartDTO.java b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/dto/SysDepartDTO.java new file mode 100644 index 0000000..ac34b50 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/dto/SysDepartDTO.java @@ -0,0 +1,42 @@ +package com.yf.exam.modules.sys.depart.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 部门信息数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-02 17:25 +*/ +@Data +@ApiModel(value="部门信息", description="部门信息") +public class SysDepartDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "1公司2部门", required=true) + private Integer deptType; + + @ApiModelProperty(value = "所属上级", required=true) + private String parentId; + + @ApiModelProperty(value = "部门名称", required=true) + private String deptName; + + @ApiModelProperty(value = "部门编码", required=true) + private String deptCode; + + @ApiModelProperty(value = "排序", required=true) + private Integer sort; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/depart/dto/request/DepartSortReqDTO.java b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/dto/request/DepartSortReqDTO.java new file mode 100644 index 0000000..92d90ab --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/dto/request/DepartSortReqDTO.java @@ -0,0 +1,28 @@ +package com.yf.exam.modules.sys.depart.dto.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 部门排序请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-03-14 10:37 +*/ +@Data +@ApiModel(value="部门排序请求类", description="部门排序请求类") +public class DepartSortReqDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "分类ID") + private String id; + + @ApiModelProperty(value = "排序,0下降,1上升") + private Integer sort; +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/depart/dto/response/SysDepartTreeDTO.java b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/dto/response/SysDepartTreeDTO.java new file mode 100644 index 0000000..ab6b9d3 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/dto/response/SysDepartTreeDTO.java @@ -0,0 +1,28 @@ +package com.yf.exam.modules.sys.depart.dto.response; + +import com.yf.exam.modules.sys.depart.dto.SysDepartDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** +*

+* 部门树结构响应类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-02 17:25 +*/ +@Data +@ApiModel(value="部门树结构响应类", description="部门树结构响应类") +public class SysDepartTreeDTO extends SysDepartDTO { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "子列表", required=true) + private List children; + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/depart/entity/SysDepart.java b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/entity/SysDepart.java new file mode 100644 index 0000000..71ca156 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/entity/SysDepart.java @@ -0,0 +1,59 @@ +package com.yf.exam.modules.sys.depart.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +/** +*

+* 部门信息实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-02 17:25 +*/ +@Data +@TableName("sys_depart") +public class SysDepart extends Model { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 1公司2部门 + */ + @TableField("dept_type") + private Integer deptType; + + /** + * 所属上级 + */ + @TableField("parent_id") + private String parentId; + + /** + * 部门名称 + */ + @TableField("dept_name") + private String deptName; + + /** + * 部门编码 + */ + @TableField("dept_code") + private String deptCode; + + /** + * 排序 + */ + private Integer sort; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/depart/mapper/SysDepartMapper.java b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/mapper/SysDepartMapper.java new file mode 100644 index 0000000..221ab89 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/mapper/SysDepartMapper.java @@ -0,0 +1,28 @@ +package com.yf.exam.modules.sys.depart.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yf.exam.modules.sys.depart.dto.SysDepartDTO; +import com.yf.exam.modules.sys.depart.dto.response.SysDepartTreeDTO; +import com.yf.exam.modules.sys.depart.entity.SysDepart; +import org.apache.ibatis.annotations.Param; + +/** +*

+* 部门信息Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-09-02 17:25 +*/ +public interface SysDepartMapper extends BaseMapper { + + /** + * 部门树分页 + * @param page + * @param query + * @return + */ + IPage paging(Page page, @Param("query") SysDepartDTO query); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/depart/service/SysDepartService.java b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/service/SysDepartService.java new file mode 100644 index 0000000..1b70e83 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/service/SysDepartService.java @@ -0,0 +1,62 @@ +package com.yf.exam.modules.sys.depart.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.sys.depart.dto.SysDepartDTO; +import com.yf.exam.modules.sys.depart.dto.response.SysDepartTreeDTO; +import com.yf.exam.modules.sys.depart.entity.SysDepart; + +import java.util.List; + +/** +*

+* 部门信息业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-02 17:25 +*/ +public interface SysDepartService extends IService { + + /** + * 保存 + * @param reqDTO + */ + void save(SysDepartDTO reqDTO); + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + /** + * 查找部门树结构 + * @return + */ + List findTree(); + + /** + * 查找部门树 + * @param ids + * @return + */ + List findTree(List ids); + + /** + * 排序 + * @param id + * @param sort + */ + void sort(String id, Integer sort); + + + /** + * 获取某个部门ID下的所有子部门ID + * @param id + * @return + */ + List listAllSubIds( String id); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/depart/service/impl/SysDepartServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/service/impl/SysDepartServiceImpl.java new file mode 100644 index 0000000..1ba267f --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/depart/service/impl/SysDepartServiceImpl.java @@ -0,0 +1,288 @@ +package com.yf.exam.modules.sys.depart.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.sys.depart.dto.SysDepartDTO; +import com.yf.exam.modules.sys.depart.dto.response.SysDepartTreeDTO; +import com.yf.exam.modules.sys.depart.entity.SysDepart; +import com.yf.exam.modules.sys.depart.mapper.SysDepartMapper; +import com.yf.exam.modules.sys.depart.service.SysDepartService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** +*

+* 部门信息业务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-02 17:25 +*/ +@Service +public class SysDepartServiceImpl extends ServiceImpl implements SysDepartService { + + + /** + * 0标识为顶级分类 + */ + private static final String ROOT_TAG = "0"; + + + @Override + public void save(SysDepartDTO reqDTO) { + + if(StringUtils.isBlank(reqDTO.getId())) { + this.fillCode(reqDTO); + }else{ + reqDTO.setSort(null); + reqDTO.setDeptCode(null); + } + + SysDepart entity = new SysDepart(); + BeanMapper.copy(reqDTO, entity); + this.saveOrUpdate(entity); + } + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + // 创建分页对象 + Page query = new Page(reqDTO.getCurrent(), reqDTO.getSize()); + + // 请求参数 + SysDepartDTO params = reqDTO.getParams(); + + //转换结果 + IPage pageData = baseMapper.paging(query, params); + + return pageData; + } + + + @Override + public List findTree() { + return this.findTree(null); + } + + @Override + public List findTree(List ids) { + + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.lambda().orderByAsc(SysDepart::getSort); + + if(!CollectionUtils.isEmpty(ids)){ + + List fullIds = new ArrayList<>(); + for(String id: ids){ + this.cycleAllParent(fullIds, id); + } + + if(!CollectionUtils.isEmpty(fullIds)){ + wrapper.lambda().in(SysDepart::getId, fullIds); + } + } + + //全部列表 + List list = this.list(wrapper); + List dtoList = BeanMapper.mapList(list, SysDepartTreeDTO.class); + + //子结构的列表 + Map> map = new HashMap<>(16); + + for(SysDepartTreeDTO item: dtoList){ + + //如果存在 + if(map.containsKey(item.getParentId())){ + map.get(item.getParentId()).add(item); + continue; + } + + //增加新的结构 + List a = new ArrayList<>(); + a.add(item); + map.put(item.getParentId(), a); + } + + //注意,第0级为顶级的 + List topList = map.get(ROOT_TAG); + if(!CollectionUtils.isEmpty(topList)){ + for(SysDepartTreeDTO item: topList){ + this.fillChildren(map, item); + } + } + + return topList; + } + + @Override + public void sort(String id, Integer sort) { + + SysDepart depart = this.getById(id); + SysDepart exchange = null; + + QueryWrapper wrapper = new QueryWrapper<>(); + // 同级排序 + wrapper.lambda() + .eq(SysDepart::getParentId, depart.getParentId()); + wrapper.last("LIMIT 1"); + + // 上升 + if(sort == 0){ + // 同级排序 + wrapper.lambda() + .lt(SysDepart::getSort, depart.getSort()) + .orderByDesc(SysDepart::getSort); + exchange = this.getOne(wrapper, false); + } + + // 下降 + if(sort == 1){ + // 同级排序 + wrapper.lambda() + .gt(SysDepart::getSort, depart.getSort()) + .orderByAsc(SysDepart::getSort); + exchange = this.getOne(wrapper, false); + } + + + if(exchange!=null) { + SysDepart a = new SysDepart(); + a.setId(id); + a.setSort(exchange.getSort()); + SysDepart b = new SysDepart(); + b.setId(exchange.getId()); + b.setSort(depart.getSort()); + this.updateById(a); + this.updateById(b); + } + } + + /** + * 获取部门编号 + * @param reqDTO + * @return + */ + private void fillCode(SysDepartDTO reqDTO){ + + // 前缀 + String code = ""; + + if(StringUtils.isNotBlank(reqDTO.getParentId()) + && !ROOT_TAG.equals(reqDTO.getParentId())){ + SysDepart parent = this.getById(reqDTO.getParentId()); + code = parent.getDeptCode(); + } + + QueryWrapper wrapper = new QueryWrapper<>(); + + // 同级排序 + wrapper.lambda() + .eq(SysDepart::getParentId, reqDTO.getParentId()) + .orderByDesc(SysDepart::getSort); + wrapper.last("LIMIT 1"); + SysDepart depart = this.getOne(wrapper, false); + + if(depart !=null){ + code += this.formatCode(depart.getSort()+1); + reqDTO.setSort(depart.getSort()+1); + }else{ + code += this.formatCode(1); + reqDTO.setSort(1); + } + + reqDTO.setDeptCode(code); + } + + /** + * 根式化加0 + * @param sort + * @return + */ + private String formatCode(Integer sort){ + if(sort < 10){ + return "A0"+sort; + } + return "A"+sort; + } + + /** + * 递归去做填充数据 + * @param map + * @param item + */ + private void fillChildren(Map> map, SysDepartTreeDTO item){ + + //设置子类 + if(map.containsKey(item.getId())){ + + List children = map.get(item.getId()); + if(!CollectionUtils.isEmpty(children)){ + for(SysDepartTreeDTO sub: children){ + this.fillChildren(map, sub); + } + } + item.setChildren(children); + } + } + + + @Override + public List listAllSubIds( String id){ + + List ids = new ArrayList<>(); + this.cycleAllSubs(ids, id); + return ids; + } + + + /** + * 递归所有子级别ID + * @param list + * @param id + */ + private void cycleAllSubs(List list, String id){ + + // 添加ID + list.add(id); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .eq(SysDepart::getParentId, id) + .orderByDesc(SysDepart::getSort); + List subList = this.list(wrapper); + if(!CollectionUtils.isEmpty(subList)){ + for(SysDepart item: subList){ + this.cycleAllSubs(list, item.getId()); + } + } + } + + /** + * 递归所有子级别ID + * @param list + * @param id + */ + private void cycleAllParent(List list, String id){ + + // 往上递归获得父类 + list.add(id); + SysDepart depart = this.getById(id); + + if(StringUtils.isNotBlank(depart.getParentId()) + && !ROOT_TAG.equals(depart.getParentId())){ + this.cycleAllParent(list, depart.getParentId()); + } + + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/system/mapper/SysDictMapper.java b/exam-api/src/main/java/com/yf/exam/modules/sys/system/mapper/SysDictMapper.java new file mode 100644 index 0000000..7910881 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/system/mapper/SysDictMapper.java @@ -0,0 +1,29 @@ +package com.yf.exam.modules.sys.system.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** +*

+* 机主信息Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-08-22 13:46 +*/ +@Mapper +public interface SysDictMapper { + + /** + * 查找数据字典 + * @param table + * @param text + * @param key + * @param value + * @return + */ + String findDict(@Param("table") String table, + @Param("text") String text, + @Param("key") String key, + @Param("value") String value); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/system/service/SysDictService.java b/exam-api/src/main/java/com/yf/exam/modules/sys/system/service/SysDictService.java new file mode 100644 index 0000000..ab8abd2 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/system/service/SysDictService.java @@ -0,0 +1,21 @@ +package com.yf.exam.modules.sys.system.service; + +/** + * 数据字典工具类 + * @author bool + */ +public interface SysDictService { + + /** + * 查找数据字典 + * @param table + * @param text + * @param key + * @param value + * @return + */ + String findDict(String table, + String text, + String key, + String value); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/system/service/impl/SysDictServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/sys/system/service/impl/SysDictServiceImpl.java new file mode 100644 index 0000000..033ed67 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/system/service/impl/SysDictServiceImpl.java @@ -0,0 +1,21 @@ +package com.yf.exam.modules.sys.system.service.impl; + +import com.yf.exam.modules.sys.system.mapper.SysDictMapper; +import com.yf.exam.modules.sys.system.service.SysDictService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @author bool + */ +@Service +public class SysDictServiceImpl implements SysDictService { + + @Autowired + private SysDictMapper sysDictMapper; + + @Override + public String findDict(String table, String text, String key, String value) { + return sysDictMapper.findDict(table, text, key, value); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/controller/SysRoleController.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/controller/SysRoleController.java new file mode 100644 index 0000000..dab2bc2 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/controller/SysRoleController.java @@ -0,0 +1,77 @@ +package com.yf.exam.modules.sys.user.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.modules.sys.user.dto.SysRoleDTO; +import com.yf.exam.modules.sys.user.entity.SysRole; +import com.yf.exam.modules.sys.user.service.SysRoleService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + *

+ * 管理用户控制器 + *

+ * + * @author 聪明笨狗 + * @since 2020-04-13 16:57 + */ +@Api(tags = {"管理用户"}) +@RestController +@RequestMapping("/exam/api/sys/role") +public class SysRoleController extends BaseController { + + @Autowired + private SysRoleService baseService; + + + + + /** + * 分页查找 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "分页查找") + @RequestMapping(value = "/paging", method = { RequestMethod.POST}) + public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.paging(reqDTO); + return super.success(page); + } + + /** + * 查找列表,每次最多返回200条数据 + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "查找列表") + @RequestMapping(value = "/list", method = { RequestMethod.POST}) + public ApiRest> list() { + + //分页查询并转换 + QueryWrapper wrapper = new QueryWrapper<>(); + + //转换并返回 + List list = baseService.list(wrapper); + + //转换数据 + List dtoList = BeanMapper.mapList(list, SysRoleDTO.class); + + return super.success(dtoList); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/controller/SysUserController.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/controller/SysUserController.java new file mode 100644 index 0000000..757c5e3 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/controller/SysUserController.java @@ -0,0 +1,182 @@ +package com.yf.exam.modules.sys.user.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import com.yf.exam.core.api.dto.BaseIdsReqDTO; +import com.yf.exam.core.api.dto.BaseStateReqDTO; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.sys.user.dto.SysUserDTO; +import com.yf.exam.modules.sys.user.dto.request.SysUserLoginReqDTO; +import com.yf.exam.modules.sys.user.dto.request.SysUserSaveReqDTO; +import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO; +import com.yf.exam.modules.sys.user.entity.SysUser; +import com.yf.exam.modules.sys.user.service.SysUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +/** + *

+ * 管理用户控制器 + *

+ * + * @author 聪明笨狗 + * @since 2020-04-13 16:57 + */ +@Api(tags = {"管理用户"}) +@RestController +@RequestMapping("/exam/api/sys/user") +public class SysUserController extends BaseController { + + @Autowired + private SysUserService baseService; + + /** + * 用户登录 + * @return + */ + @CrossOrigin + @ApiOperation(value = "用户登录") + @RequestMapping(value = "/login", method = {RequestMethod.POST}) + public ApiRest login(@RequestBody SysUserLoginReqDTO reqDTO) { + SysUserLoginDTO respDTO = baseService.login(reqDTO.getUsername(), reqDTO.getPassword()); + return super.success(respDTO); + } + + /** + * 用户登录 + * @return + */ + @CrossOrigin + @ApiOperation(value = "用户登录") + @RequestMapping(value = "/logout", method = {RequestMethod.POST}) + public ApiRest logout(HttpServletRequest request) { + String token = request.getHeader("token"); + System.out.println("+++++当前会话为:"+token); + baseService.logout(token); + return super.success(); + } + + /** + * 获取会话 + * @return + */ + @ApiOperation(value = "获取会话") + @RequestMapping(value = "/info", method = {RequestMethod.POST}) + public ApiRest info(@RequestParam("token") String token) { + SysUserLoginDTO respDTO = baseService.token(token); + return success(respDTO); + } + + /** + * 修改用户资料 + * @return + */ + @ApiOperation(value = "修改用户资料") + @RequestMapping(value = "/update", method = {RequestMethod.POST}) + public ApiRest update(@RequestBody SysUserDTO reqDTO) { + baseService.update(reqDTO); + return success(); + } + + + /** + * 保存或修改系统用户 + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "保存或修改") + @RequestMapping(value = "/save", method = {RequestMethod.POST}) + public ApiRest save(@RequestBody SysUserSaveReqDTO reqDTO) { + baseService.save(reqDTO); + return success(); + } + + + /** + * 批量删除 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "批量删除") + @RequestMapping(value = "/delete", method = { RequestMethod.POST}) + public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) { + //根据ID删除 + baseService.removeByIds(reqDTO.getIds()); + return super.success(); + } + + /** + * 分页查找 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "分页查找") + @RequestMapping(value = "/paging", method = { RequestMethod.POST}) + public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.paging(reqDTO); + return super.success(page); + } + + /** + * 修改状态 + * @param reqDTO + * @return + */ + @RequiresRoles("sa") + @ApiOperation(value = "修改状态") + @RequestMapping(value = "/state", method = { RequestMethod.POST}) + public ApiRest state(@RequestBody BaseStateReqDTO reqDTO) { + + // 条件 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .in(SysUser::getId, reqDTO.getIds()) + .ne(SysUser::getUserName, "admin"); + + + SysUser record = new SysUser(); + record.setState(reqDTO.getState()); + baseService.update(record, wrapper); + + return super.success(); + } + + + /** + * 保存或修改系统用户 + * @return + */ + @ApiOperation(value = "学员注册") + @RequestMapping(value = "/reg", method = {RequestMethod.POST}) + public ApiRest reg(@RequestBody SysUserDTO reqDTO) { + SysUserLoginDTO respDTO = baseService.reg(reqDTO); + return success(respDTO); + } + + /** + * 快速注册,如果手机号存在则登录,不存在就注册 + * @return + */ + @ApiOperation(value = "快速注册") + @RequestMapping(value = "/quick-reg", method = {RequestMethod.POST}) + public ApiRest quick(@RequestBody SysUserDTO reqDTO) { + SysUserLoginDTO respDTO = baseService.quickReg(reqDTO); + return success(respDTO); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/SysRoleDTO.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/SysRoleDTO.java new file mode 100644 index 0000000..d138569 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/SysRoleDTO.java @@ -0,0 +1,30 @@ +package com.yf.exam.modules.sys.user.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 角色请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Data +@ApiModel(value="角色", description="角色") +public class SysRoleDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "角色ID", required=true) + private String id; + + @ApiModelProperty(value = "角色名称", required=true) + private String roleName; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/SysUserDTO.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/SysUserDTO.java new file mode 100644 index 0000000..e8831b3 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/SysUserDTO.java @@ -0,0 +1,55 @@ +package com.yf.exam.modules.sys.user.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +*

+* 管理用户请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Data +@ApiModel(value="管理用户", description="管理用户") +public class SysUserDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "用户名", required=true) + private String userName; + + @ApiModelProperty(value = "真实姓名", required=true) + private String realName; + + @ApiModelProperty(value = "密码", required=true) + private String password; + + @ApiModelProperty(value = "密码盐", required=true) + private String salt; + + @ApiModelProperty(value = "角色列表", required=true) + private String roleIds; + + @ApiModelProperty(value = "部门ID", required=true) + private String departId; + + @ApiModelProperty(value = "创建时间", required=true) + private Date createTime; + + @ApiModelProperty(value = "更新时间", required=true) + private Date updateTime; + + @ApiModelProperty(value = "状态", required=true) + private Integer state; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/SysUserRoleDTO.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/SysUserRoleDTO.java new file mode 100644 index 0000000..0973302 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/SysUserRoleDTO.java @@ -0,0 +1,33 @@ +package com.yf.exam.modules.sys.user.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 用户角色请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Data +@ApiModel(value="用户角色", description="用户角色") +public class SysUserRoleDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "用户ID", required=true) + private String userId; + + @ApiModelProperty(value = "角色ID", required=true) + private String roleId; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/request/SysUserLoginReqDTO.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/request/SysUserLoginReqDTO.java new file mode 100644 index 0000000..35f00a7 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/request/SysUserLoginReqDTO.java @@ -0,0 +1,29 @@ +package com.yf.exam.modules.sys.user.dto.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 管理员登录请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Data +@ApiModel(value="管理员登录请求类", description="管理员登录请求类") +public class SysUserLoginReqDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "用户名", required=true) + private String username; + + @ApiModelProperty(value = "密码", required=true) + private String password; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/request/SysUserSaveReqDTO.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/request/SysUserSaveReqDTO.java new file mode 100644 index 0000000..e3fb9f4 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/request/SysUserSaveReqDTO.java @@ -0,0 +1,43 @@ +package com.yf.exam.modules.sys.user.dto.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** +*

+* 管理员登录请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Data +@ApiModel(value="管理员保存请求类", description="管理员保存请求类") +public class SysUserSaveReqDTO implements Serializable { + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "用户名", required=true) + private String userName; + + @ApiModelProperty(value = "头像", required=true) + private String avatar; + + @ApiModelProperty(value = "真实姓名", required=true) + private String realName; + + @ApiModelProperty(value = "密码", required=true) + private String password; + + @ApiModelProperty(value = "部门", required=true) + private String departId; + + @ApiModelProperty(value = "角色列表", required=true) + private List roles; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/request/SysUserTokenReqDTO.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/request/SysUserTokenReqDTO.java new file mode 100644 index 0000000..5e830a9 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/request/SysUserTokenReqDTO.java @@ -0,0 +1,26 @@ +package com.yf.exam.modules.sys.user.dto.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** +*

+* 会话检查请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Data +@ApiModel(value="会话检查请求类", description="会话检查请求类") +public class SysUserTokenReqDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "用户名", required=true) + private String token; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/response/SysUserLoginDTO.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/response/SysUserLoginDTO.java new file mode 100644 index 0000000..17362b5 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/dto/response/SysUserLoginDTO.java @@ -0,0 +1,55 @@ +package com.yf.exam.modules.sys.user.dto.response; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** +*

+* 管理用户请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Data +@ApiModel(value="管理用户登录响应类", description="管理用户登录响应类") +public class SysUserLoginDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "用户名", required=true) + private String userName; + + @ApiModelProperty(value = "真实姓名", required=true) + private String realName; + + @ApiModelProperty(value = "角色列表", required=true) + private String roleIds; + + @ApiModelProperty(value = "部门ID", required=true) + private String departId; + + @ApiModelProperty(value = "创建时间", required=true) + private Date createTime; + + @ApiModelProperty(value = "更新时间", required=true) + private Date updateTime; + + @ApiModelProperty(value = "状态", required=true) + private Integer state; + + @ApiModelProperty(value = "角色列表", required=true) + private List roles; + + @ApiModelProperty(value = "登录令牌", required=true) + private String token; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/entity/SysRole.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/entity/SysRole.java new file mode 100644 index 0000000..b5d328f --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/entity/SysRole.java @@ -0,0 +1,36 @@ +package com.yf.exam.modules.sys.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +/** +*

+* 角色实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Data +@TableName("sys_role") +public class SysRole extends Model { + + private static final long serialVersionUID = 1L; + + /** + * 角色ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 角色名称 + */ + @TableField("role_name") + private String roleName; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/entity/SysUser.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/entity/SysUser.java new file mode 100644 index 0000000..7651f68 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/entity/SysUser.java @@ -0,0 +1,83 @@ +package com.yf.exam.modules.sys.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +import java.util.Date; + +/** +*

+* 管理用户实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Data +@TableName("sys_user") +public class SysUser extends Model { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 用户名 + */ + @TableField("user_name") + private String userName; + + /** + * 真实姓名 + */ + @TableField("real_name") + private String realName; + + /** + * 密码 + */ + private String password; + + /** + * 密码盐 + */ + private String salt; + + /** + * 角色列表 + */ + @TableField("role_ids") + private String roleIds; + + /** + * 部门ID + */ + @TableField("depart_id") + private String departId; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + + /** + * 状态 + */ + private Integer state; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/entity/SysUserRole.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/entity/SysUserRole.java new file mode 100644 index 0000000..1006e0f --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/entity/SysUserRole.java @@ -0,0 +1,42 @@ +package com.yf.exam.modules.sys.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +/** +*

+* 用户角色实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Data +@TableName("sys_user_role") +public class SysUserRole extends Model { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 用户ID + */ + @TableField("user_id") + private String userId; + + /** + * 角色ID + */ + @TableField("role_id") + private String roleId; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/mapper/SysRoleMapper.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/mapper/SysRoleMapper.java new file mode 100644 index 0000000..05763a3 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/mapper/SysRoleMapper.java @@ -0,0 +1,15 @@ +package com.yf.exam.modules.sys.user.mapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.sys.user.entity.SysRole; + +/** +*

+* 角色Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +public interface SysRoleMapper extends BaseMapper { + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/mapper/SysUserMapper.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/mapper/SysUserMapper.java new file mode 100644 index 0000000..de7ae4f --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/mapper/SysUserMapper.java @@ -0,0 +1,16 @@ +package com.yf.exam.modules.sys.user.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.sys.user.entity.SysUser; + +/** +*

+* 管理用户Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +public interface SysUserMapper extends BaseMapper { + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/mapper/SysUserRoleMapper.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..3055949 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/mapper/SysUserRoleMapper.java @@ -0,0 +1,16 @@ +package com.yf.exam.modules.sys.user.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.sys.user.entity.SysUserRole; + +/** +*

+* 用户角色Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +public interface SysUserRoleMapper extends BaseMapper { + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/SysRoleService.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/SysRoleService.java new file mode 100644 index 0000000..b0a87f9 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/SysRoleService.java @@ -0,0 +1,25 @@ +package com.yf.exam.modules.sys.user.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.modules.sys.user.dto.SysRoleDTO; +import com.yf.exam.modules.sys.user.entity.SysRole; +import com.yf.exam.core.api.dto.PagingReqDTO; + +/** +*

+* 角色业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +public interface SysRoleService extends IService { + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/SysUserRoleService.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/SysUserRoleService.java new file mode 100644 index 0000000..76fb6f2 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/SysUserRoleService.java @@ -0,0 +1,61 @@ +package com.yf.exam.modules.sys.user.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.modules.sys.user.dto.SysUserRoleDTO; +import com.yf.exam.modules.sys.user.entity.SysUserRole; +import com.yf.exam.core.api.dto.PagingReqDTO; + +import java.util.List; + +/** +*

+* 用户角色业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +public interface SysUserRoleService extends IService { + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + /** + * 查找用户角色列表 + * @param userId + * @return + */ + List listRoles(String userId); + + /** + * 保存全部角色 + * @param userId + * @param ids + * @return + */ + String saveRoles(String userId, List ids); + + /** + * 是否学生 + * @param userId + * @return + */ + boolean isStudent(String userId); + + /** + * 是否老师 + */ + boolean isTeacher(String userId); + + /** + * 是否管理 + * @param userId + * @return + */ + boolean isAdmin(String userId); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/SysUserService.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/SysUserService.java new file mode 100644 index 0000000..52a1818 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/SysUserService.java @@ -0,0 +1,72 @@ +package com.yf.exam.modules.sys.user.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.modules.sys.user.dto.SysUserDTO; +import com.yf.exam.modules.sys.user.dto.request.SysUserSaveReqDTO; +import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO; +import com.yf.exam.modules.sys.user.entity.SysUser; +import com.yf.exam.core.api.dto.PagingReqDTO; + +/** +*

+* 管理用户业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +public interface SysUserService extends IService { + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + /** + * 登录 + * @param userName + * @param password + * @return + */ + SysUserLoginDTO login(String userName, String password); + + /** + * 获取管理会话 + * @param token + * @return + */ + SysUserLoginDTO token(String token); + + /** + * 退出登录 + * @param token + */ + void logout(String token); + + /** + * 修改用户资料 + * @param reqDTO + */ + void update(SysUserDTO reqDTO); + + /** + * 保存添加系统用户 + * @param reqDTO + */ + void save(SysUserSaveReqDTO reqDTO); + + /** + * 用户注册 + * @param reqDTO + */ + SysUserLoginDTO reg(SysUserDTO reqDTO); + + /** + * 快速注册 + * @param reqDTO + */ + SysUserLoginDTO quickReg(SysUserDTO reqDTO); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/impl/SysRoleServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..8593f55 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,42 @@ +package com.yf.exam.modules.sys.user.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.modules.sys.user.dto.SysRoleDTO; +import com.yf.exam.modules.sys.user.entity.SysRole; +import com.yf.exam.modules.sys.user.mapper.SysRoleMapper; +import com.yf.exam.modules.sys.user.service.SysRoleService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import org.springframework.stereotype.Service; + +/** +*

+* 语言设置 服务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Service +public class SysRoleServiceImpl extends ServiceImpl implements SysRoleService { + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + //创建分页对象 + IPage query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); + + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + + //获得数据 + IPage page = this.page(query, wrapper); + //转换结果 + IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); + return pageData; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/impl/SysUserRoleServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/impl/SysUserRoleServiceImpl.java new file mode 100644 index 0000000..794457b --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/impl/SysUserRoleServiceImpl.java @@ -0,0 +1,128 @@ +package com.yf.exam.modules.sys.user.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.modules.sys.user.dto.SysUserRoleDTO; +import com.yf.exam.modules.sys.user.entity.SysUserRole; +import com.yf.exam.modules.sys.user.mapper.SysUserRoleMapper; +import com.yf.exam.modules.sys.user.service.SysUserRoleService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +/** +*

+* 语言设置 服务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Service +public class SysUserRoleServiceImpl extends ServiceImpl implements SysUserRoleService { + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + //创建分页对象 + IPage query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); + + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + + //获得数据 + IPage page = this.page(query, wrapper); + //转换结果 + IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); + return pageData; + } + + @Override + public List listRoles(String userId) { + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(SysUserRole::getUserId, userId); + + List list = this.list(wrapper); + List roles = new ArrayList<>(); + if(!CollectionUtils.isEmpty(list)){ + for(SysUserRole item: list){ + roles.add(item.getRoleId()); + } + } + + return roles; + } + + @Override + public String saveRoles(String userId, List ids) { + + // 删除全部角色 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(SysUserRole::getUserId, userId); + this.remove(wrapper); + + + if(!CollectionUtils.isEmpty(ids)){ + + List list = new ArrayList<>(); + String roleIds = null; + + for(String item: ids){ + SysUserRole role = new SysUserRole(); + role.setRoleId(item); + role.setUserId(userId); + list.add(role); + if(StringUtils.isEmpty(roleIds)){ + roleIds = item; + }else{ + roleIds+=","+item; + } + } + + this.saveBatch(list); + return roleIds; + } + + return ""; + } + + @Override + public boolean isStudent(String userId) { + + // 学生角色 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(SysUserRole::getUserId, userId) + .eq(SysUserRole::getRoleId, "student"); + + return this.count(wrapper) > 0; + } + + @Override + public boolean isTeacher(String userId) { + // 学生角色 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(SysUserRole::getUserId, userId) + .eq(SysUserRole::getRoleId, "teacher"); + + return this.count(wrapper) > 0; + } + + @Override + public boolean isAdmin(String userId) { + // 学生角色 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(SysUserRole::getUserId, userId) + .eq(SysUserRole::getRoleId, "sa"); + + return this.count(wrapper) > 0; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/impl/SysUserServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..4370e56 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/sys/user/service/impl/SysUserServiceImpl.java @@ -0,0 +1,253 @@ +package com.yf.exam.modules.sys.user.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.api.ApiError; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.core.enums.CommonState; +import com.yf.exam.core.exception.ServiceException; +import com.yf.exam.core.utils.BeanMapper; +import com.yf.exam.core.utils.passwd.PassHandler; +import com.yf.exam.core.utils.passwd.PassInfo; +import com.yf.exam.ability.shiro.jwt.JwtUtils; +import com.yf.exam.modules.sys.user.dto.SysUserDTO; +import com.yf.exam.modules.sys.user.dto.request.SysUserSaveReqDTO; +import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO; +import com.yf.exam.modules.sys.user.entity.SysUser; +import com.yf.exam.modules.sys.user.mapper.SysUserMapper; +import com.yf.exam.modules.sys.user.service.SysUserRoleService; +import com.yf.exam.modules.sys.user.service.SysUserService; +import com.yf.exam.modules.user.UserUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; + +/** +*

+* 语言设置 服务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-04-13 16:57 +*/ +@Service +public class SysUserServiceImpl extends ServiceImpl implements SysUserService { + + @Autowired + private SysUserRoleService sysUserRoleService; + + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + //创建分页对象 + IPage query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); + + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + + SysUserDTO params = reqDTO.getParams(); + + if(params!=null){ + if(!StringUtils.isBlank(params.getUserName())){ + wrapper.lambda().like(SysUser::getUserName, params.getUserName()); + } + + if(!StringUtils.isBlank(params.getRealName())){ + wrapper.lambda().like(SysUser::getRealName, params.getRealName()); + } + } + + //获得数据 + IPage page = this.page(query, wrapper); + //转换结果 + IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); + return pageData; + } + + @Override + public SysUserLoginDTO login(String userName, String password) { + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(SysUser::getUserName, userName); + + SysUser user = this.getOne(wrapper, false); + if(user == null){ + throw new ServiceException(ApiError.ERROR_90010002); + } + + // 被禁用 + if(user.getState().equals(CommonState.ABNORMAL)){ + throw new ServiceException(ApiError.ERROR_90010005); + } + + boolean check = PassHandler.checkPass(password,user.getSalt(), user.getPassword()); + if(!check){ + throw new ServiceException(ApiError.ERROR_90010002); + } + + return this.setToken(user); + } + + @Override + public SysUserLoginDTO token(String token) { + + // 获得会话 + String username = JwtUtils.getUsername(token); + + // 校验结果 + boolean check = JwtUtils.verify(token, username); + + if(!check){ + throw new ServiceException(ApiError.ERROR_90010002); + } + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(SysUser::getUserName, username); + + SysUser user = this.getOne(wrapper, false); + if(user == null){ + throw new ServiceException(ApiError.ERROR_10010002); + } + + // 被禁用 + if(user.getState().equals(CommonState.ABNORMAL)){ + throw new ServiceException(ApiError.ERROR_90010005); + } + + return this.setToken(user); + } + + @Override + public void logout(String token) { + + // 仅退出当前会话 + SecurityUtils.getSubject().logout(); + } + + @Override + public void update(SysUserDTO reqDTO) { + + + String pass = reqDTO.getPassword(); + if(!StringUtils.isBlank(pass)){ + PassInfo passInfo = PassHandler.buildPassword(pass); + SysUser user = this.getById(UserUtils.getUserId()); + user.setPassword(passInfo.getPassword()); + user.setSalt(passInfo.getSalt()); + this.updateById(user); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void save(SysUserSaveReqDTO reqDTO) { + + List roles = reqDTO.getRoles(); + + if(CollectionUtils.isEmpty(roles)){ + throw new ServiceException(ApiError.ERROR_90010003); + } + + // 保存基本信息 + SysUser user = new SysUser(); + BeanMapper.copy(reqDTO, user); + + // 添加模式 + if(StringUtils.isBlank(user.getId())){ + user.setId(IdWorker.getIdStr()); + } + + // 修改密码 + if(!StringUtils.isBlank(reqDTO.getPassword())){ + PassInfo pass = PassHandler.buildPassword(reqDTO.getPassword()); + user.setPassword(pass.getPassword()); + user.setSalt(pass.getSalt()); + } + + // 保存角色信息 + String roleIds = sysUserRoleService.saveRoles(user.getId(), roles); + user.setRoleIds(roleIds); + this.saveOrUpdate(user); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public SysUserLoginDTO reg(SysUserDTO reqDTO) { + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(SysUser::getUserName, reqDTO.getUserName()); + + int count = this.count(wrapper); + + if(count > 0){ + throw new ServiceException(1, "用户名已存在,换一个吧!"); + } + + + // 保存用户 + SysUser user = new SysUser(); + user.setId(IdWorker.getIdStr()); + user.setUserName(reqDTO.getUserName()); + user.setRealName(reqDTO.getRealName()); + PassInfo passInfo = PassHandler.buildPassword(reqDTO.getPassword()); + user.setPassword(passInfo.getPassword()); + user.setSalt(passInfo.getSalt()); + + // 保存角色 + List roles = new ArrayList<>(); + roles.add("student"); + String roleIds = sysUserRoleService.saveRoles(user.getId(), roles); + user.setRoleIds(roleIds); + this.save(user); + + return this.setToken(user); + } + + @Override + public SysUserLoginDTO quickReg(SysUserDTO reqDTO) { + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(SysUser::getUserName, reqDTO.getUserName()); + wrapper.last(" LIMIT 1 "); + SysUser user = this.getOne(wrapper); + if(user!=null){ + return this.setToken(user); + } + + return this.reg(reqDTO); + } + + + /** + * 保存会话信息 + * @param user + * @return + */ + private SysUserLoginDTO setToken(SysUser user){ + + SysUserLoginDTO respDTO = new SysUserLoginDTO(); + BeanMapper.copy(user, respDTO); + + // 生成Token + String token = JwtUtils.sign(user.getUserName()); + respDTO.setToken(token); + + // 填充角色 + List roles = sysUserRoleService.listRoles(user.getId()); + respDTO.setRoles(roles); + + return respDTO; + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/UserUtils.java b/exam-api/src/main/java/com/yf/exam/modules/user/UserUtils.java new file mode 100644 index 0000000..4477ecb --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/UserUtils.java @@ -0,0 +1,56 @@ +package com.yf.exam.modules.user; + +import com.yf.exam.core.api.ApiError; +import com.yf.exam.core.exception.ServiceException; +import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO; +import org.apache.shiro.SecurityUtils; + +/** + * 用户静态工具类 + * @author bool + */ +public class UserUtils { + + + /** + * 获取当前登录用户的ID + * @param throwable + * @return + */ + public static String getUserId(boolean throwable){ + try { + return ((SysUserLoginDTO) SecurityUtils.getSubject().getPrincipal()).getId(); + }catch (Exception e){ + if(throwable){ + throw new ServiceException(ApiError.ERROR_10010002); + } + return null; + } + } + + /** + * 获取当前登录用户的ID + * @param throwable + * @return + */ + public static boolean isAdmin(boolean throwable){ + try { + SysUserLoginDTO dto = ((SysUserLoginDTO) SecurityUtils.getSubject().getPrincipal()); + return dto.getRoles().contains("sa"); + }catch (Exception e){ + if(throwable){ + throw new ServiceException(ApiError.ERROR_10010002); + } + } + + return false; + } + + /** + * 获取当前登录用户的ID,默认是会抛异常的 + * @return + */ + public static String getUserId(){ + return getUserId(true); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/book/controller/UserBookController.java b/exam-api/src/main/java/com/yf/exam/modules/user/book/controller/UserBookController.java new file mode 100644 index 0000000..9300493 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/book/controller/UserBookController.java @@ -0,0 +1,76 @@ +package com.yf.exam.modules.user.book.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import com.yf.exam.core.api.dto.BaseIdRespDTO; +import com.yf.exam.core.api.dto.BaseIdsReqDTO; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.user.book.dto.UserBookDTO; +import com.yf.exam.modules.user.book.service.UserBookService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** +*

+* 错题本控制器 +*

+* +* @author 聪明笨狗 +* @since 2020-05-27 17:56 +*/ +@Api(tags={"错题本"}) +@RestController +@RequestMapping("/exam/api/user/wrong-book") +public class UserBookController extends BaseController { + + @Autowired + private UserBookService baseService; + + + /** + * 批量删除 + * @param reqDTO + * @return + */ + @ApiOperation(value = "批量删除") + @RequestMapping(value = "/delete", method = { RequestMethod.POST}) + public ApiRest delete(@RequestBody BaseIdsReqDTO reqDTO) { + //根据ID删除 + baseService.removeByIds(reqDTO.getIds()); + return super.success(); + } + + /** + * 分页查找 + * @param reqDTO + * @return + */ + @ApiOperation(value = "分页查找") + @RequestMapping(value = "/paging", method = { RequestMethod.POST}) + public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.paging(reqDTO); + + return super.success(page); + } + + /** + * 查找列表,每次最多返回200条数据 + * @param reqDTO + * @return + */ + @ApiOperation(value = "查找列表") + @RequestMapping(value = "/next", method = { RequestMethod.POST}) + public ApiRest nextQu(@RequestBody UserBookDTO reqDTO) { + //转换并返回 + String quId = baseService.findNext(reqDTO.getExamId(), reqDTO.getQuId()); + return super.success(new BaseIdRespDTO(quId)); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/book/dto/UserBookDTO.java b/exam-api/src/main/java/com/yf/exam/modules/user/book/dto/UserBookDTO.java new file mode 100644 index 0000000..582b415 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/book/dto/UserBookDTO.java @@ -0,0 +1,52 @@ +package com.yf.exam.modules.user.book.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +*

+* 错题本请求类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-27 17:56 +*/ +@Data +@ApiModel(value="错题本", description="错题本") +public class UserBookDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "ID", required=true) + private String id; + + @ApiModelProperty(value = "考试ID", required=true) + private String examId; + + @ApiModelProperty(value = "用户ID", required=true) + private String userId; + + @ApiModelProperty(value = "题目ID", required=true) + private String quId; + + @ApiModelProperty(value = "加入时间", required=true) + private Date createTime; + + @ApiModelProperty(value = "最近错误时间", required=true) + private Date updateTime; + + @ApiModelProperty(value = "错误时间", required=true) + private Integer wrongCount; + + @ApiModelProperty(value = "题目标题", required=true) + private String title; + + @ApiModelProperty(value = "错题序号", required=true) + private Integer sort; + +} \ No newline at end of file diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/book/entity/UserBook.java b/exam-api/src/main/java/com/yf/exam/modules/user/book/entity/UserBook.java new file mode 100644 index 0000000..f163425 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/book/entity/UserBook.java @@ -0,0 +1,78 @@ +package com.yf.exam.modules.user.book.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +import java.util.Date; + +/** +*

+* 错题本实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-27 17:56 +*/ +@Data +@TableName("el_user_book") +public class UserBook extends Model { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 考试ID + */ + @TableField("exam_id") + private String examId; + + /** + * 用户ID + */ + @TableField("user_id") + private String userId; + + /** + * 题目ID + */ + @TableField("qu_id") + private String quId; + + /** + * 加入时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 最近错误时间 + */ + @TableField("update_time") + private Date updateTime; + + /** + * 错误时间 + */ + @TableField("wrong_count") + private Integer wrongCount; + + /** + * 题目标题 + */ + private String title; + + /** + * 错题序号 + */ + private Integer sort; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/book/mapper/UserBookMapper.java b/exam-api/src/main/java/com/yf/exam/modules/user/book/mapper/UserBookMapper.java new file mode 100644 index 0000000..67c3cca --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/book/mapper/UserBookMapper.java @@ -0,0 +1,16 @@ +package com.yf.exam.modules.user.book.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yf.exam.modules.user.book.entity.UserBook; + +/** +*

+* 错题本Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-05-27 17:56 +*/ +public interface UserBookMapper extends BaseMapper { + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/book/service/UserBookService.java b/exam-api/src/main/java/com/yf/exam/modules/user/book/service/UserBookService.java new file mode 100644 index 0000000..726a070 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/book/service/UserBookService.java @@ -0,0 +1,40 @@ +package com.yf.exam.modules.user.book.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.user.book.dto.UserBookDTO; +import com.yf.exam.modules.user.book.entity.UserBook; + +/** +*

+* 错题本业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-27 17:56 +*/ +public interface UserBookService extends IService { + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + /** + * 加入错题本 + * @param quId + * @param examId + */ + void addBook(String examId, String quId); + + /** + * 查找第一个错题 + * @param quId + * @param examId + * @return + */ + String findNext(String examId, String quId); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/book/service/impl/UserBookServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/user/book/service/impl/UserBookServiceImpl.java new file mode 100644 index 0000000..e1f7de6 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/book/service/impl/UserBookServiceImpl.java @@ -0,0 +1,155 @@ +package com.yf.exam.modules.user.book.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.qu.entity.Qu; +import com.yf.exam.modules.qu.service.QuService; +import com.yf.exam.modules.user.UserUtils; +import com.yf.exam.modules.user.book.dto.UserBookDTO; +import com.yf.exam.modules.user.book.entity.UserBook; +import com.yf.exam.modules.user.book.mapper.UserBookMapper; +import com.yf.exam.modules.user.book.service.UserBookService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +/** +*

+* 语言设置 服务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-05-27 17:56 +*/ +@Service +public class UserBookServiceImpl extends ServiceImpl implements UserBookService { + + @Autowired + private QuService quService; + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + //创建分页对象 + Page query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize()); + + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + // 查找用户的错题 + wrapper.lambda().eq(UserBook::getUserId, UserUtils.getUserId(true)); + + UserBookDTO params = reqDTO.getParams(); + if(params!=null){ + if(!StringUtils.isEmpty(params.getTitle())){ + wrapper.lambda().like(UserBook::getTitle, params.getTitle()); + } + + if(!StringUtils.isEmpty(params.getExamId())){ + wrapper.lambda().eq(UserBook::getExamId, params.getExamId()); + } + } + + //获得数据 + IPage page = this.page(query, wrapper); + //转换结果 + IPage pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference>(){}); + return pageData; + } + + + + @Override + public void addBook(String examId, String quId) { + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .eq(UserBook::getUserId, UserUtils.getUserId()) + .eq(UserBook::getExamId, examId) + .eq(UserBook::getQuId, quId); + + //查找已有的错题信息 + UserBook book = this.getOne(wrapper, false); + + + // 问题 + Qu qu = quService.getById(quId); + + if (book == null) { + book = new UserBook(); + book.setExamId(examId); + book.setUserId(UserUtils.getUserId()); + book.setTitle(qu.getContent()); + book.setQuId(quId); + book.setWrongCount(1); + Integer maxSort = this.findMaxSort(examId, UserUtils.getUserId()); + book.setSort(maxSort+1); + + this.save(book); + } else { + book.setWrongCount(book.getWrongCount()+1); + this.updateById(book); + } + } + + @Override + public String findNext(String examId, String quId) { + + + Integer sort = 999999; + + if(!StringUtils.isEmpty(quId)){ + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .eq(UserBook::getUserId, UserUtils.getUserId()) + .eq(UserBook::getExamId, examId) + .eq(UserBook::getQuId, quId); + wrapper.last(" ORDER BY `sort` DESC"); + + UserBook last = this.getOne(wrapper, false); + if(last!=null){ + sort = last.getSort(); + } + } + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .eq(UserBook::getUserId, UserUtils.getUserId()) + .eq(UserBook::getExamId, examId) + .lt(UserBook::getSort, sort); + wrapper.last(" ORDER BY `sort` DESC"); + + UserBook next = this.getOne(wrapper, false); + if(next != null){ + return next.getQuId(); + } + + return null; + } + + /** + * 查找最大的排序 + * @param userId + * @return + */ + private Integer findMaxSort(String examId, String userId){ + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda() + .eq(UserBook::getExamId, examId) + .eq(UserBook::getUserId, userId); + wrapper.last(" ORDER BY `sort` DESC"); + + UserBook book = this.getOne(wrapper, false); + if(book == null){ + return 0; + } + return book.getSort(); + } + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/exam/controller/UserExamController.java b/exam-api/src/main/java/com/yf/exam/modules/user/exam/controller/UserExamController.java new file mode 100644 index 0000000..65cf11c --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/exam/controller/UserExamController.java @@ -0,0 +1,65 @@ +package com.yf.exam.modules.user.exam.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.yf.exam.core.api.ApiRest; +import com.yf.exam.core.api.controller.BaseController; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.user.exam.dto.request.UserExamReqDTO; +import com.yf.exam.modules.user.exam.dto.response.UserExamRespDTO; +import com.yf.exam.modules.user.exam.service.UserExamService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** +*

+* 考试记录控制器 +*

+* +* @author 聪明笨狗 +* @since 2020-09-21 15:13 +*/ +@Api(tags={"考试记录"}) +@RestController +@RequestMapping("/exam/api/user/exam") +public class UserExamController extends BaseController { + + @Autowired + private UserExamService baseService; + + + /** + * 分页查找 + * @param reqDTO + * @return + */ + @ApiOperation(value = "分页查找") + @RequestMapping(value = "/paging", method = { RequestMethod.POST}) + public ApiRest> paging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.paging(reqDTO); + + return super.success(page); + } + + + /** + * 分页查找 + * @param reqDTO + * @return + */ + @ApiOperation(value = "分页查找") + @RequestMapping(value = "/my-paging", method = { RequestMethod.POST}) + public ApiRest> myPaging(@RequestBody PagingReqDTO reqDTO) { + + //分页查询并转换 + IPage page = baseService.myPaging(reqDTO); + + return super.success(page); + } +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/exam/dto/UserExamDTO.java b/exam-api/src/main/java/com/yf/exam/modules/user/exam/dto/UserExamDTO.java new file mode 100644 index 0000000..61a0c23 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/exam/dto/UserExamDTO.java @@ -0,0 +1,50 @@ +package com.yf.exam.modules.user.exam.dto; + +import com.yf.exam.core.annon.Dict; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import java.util.Date; + +import java.io.Serializable; + +/** +*

+* 考试记录数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-21 15:13 +*/ +@Data +@ApiModel(value="考试记录", description="考试记录") +public class UserExamDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + + private String id; + + @ApiModelProperty(value = "用户ID", required=true) + private String userId; + + @Dict(dictTable = "el_exam", dicText = "title", dicCode = "id") + @ApiModelProperty(value = "考试ID", required=true) + private String examId; + + @ApiModelProperty(value = "考试次数", required=true) + private Integer tryCount; + + @ApiModelProperty(value = "最高分数", required=true) + private Integer maxScore; + + @ApiModelProperty(value = "是否通过", required=true) + private Boolean passed; + + @ApiModelProperty(value = "创建时间") + private Date createTime; + + @ApiModelProperty(value = "更新时间") + private Date updateTime; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/exam/dto/request/UserExamReqDTO.java b/exam-api/src/main/java/com/yf/exam/modules/user/exam/dto/request/UserExamReqDTO.java new file mode 100644 index 0000000..e554837 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/exam/dto/request/UserExamReqDTO.java @@ -0,0 +1,30 @@ +package com.yf.exam.modules.user.exam.dto.request; + +import com.yf.exam.modules.user.exam.dto.UserExamDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** +*

+* 考试记录数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-21 15:13 +*/ +@Data +@ApiModel(value="考试记录", description="考试记录") +public class UserExamReqDTO extends UserExamDTO { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "考试名称", required=true) + private String title; + + @ApiModelProperty(value = "人员名称", required=true) + private String realName; + + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/exam/dto/response/UserExamRespDTO.java b/exam-api/src/main/java/com/yf/exam/modules/user/exam/dto/response/UserExamRespDTO.java new file mode 100644 index 0000000..0ad1d39 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/exam/dto/response/UserExamRespDTO.java @@ -0,0 +1,29 @@ +package com.yf.exam.modules.user.exam.dto.response; + +import com.yf.exam.modules.user.exam.dto.UserExamDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** +*

+* 考试记录数据传输类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-21 15:13 +*/ +@Data +@ApiModel(value="考试记录", description="考试记录") +public class UserExamRespDTO extends UserExamDTO { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "考试名称", required=true) + private String title; + + @ApiModelProperty(value = "人员名称", required=true) + private String realName; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/exam/entity/UserExam.java b/exam-api/src/main/java/com/yf/exam/modules/user/exam/entity/UserExam.java new file mode 100644 index 0000000..7721d40 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/exam/entity/UserExam.java @@ -0,0 +1,69 @@ +package com.yf.exam.modules.user.exam.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; +import java.util.Date; + +/** +*

+* 考试记录实体类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-21 15:13 +*/ +@Data +@TableName("el_user_exam") +public class UserExam extends Model { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 用户ID + */ + @TableField("user_id") + private String userId; + + /** + * 考试ID + */ + @TableField("exam_id") + private String examId; + + /** + * 考试次数 + */ + @TableField("try_count") + private Integer tryCount; + + /** + * 最高分数 + */ + @TableField("max_score") + private Integer maxScore; + + /** + * 是否通过 + */ + private Boolean passed; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/exam/mapper/UserExamMapper.java b/exam-api/src/main/java/com/yf/exam/modules/user/exam/mapper/UserExamMapper.java new file mode 100644 index 0000000..c28ce6f --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/exam/mapper/UserExamMapper.java @@ -0,0 +1,29 @@ +package com.yf.exam.modules.user.exam.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yf.exam.modules.user.exam.dto.request.UserExamReqDTO; +import com.yf.exam.modules.user.exam.dto.response.UserExamRespDTO; +import com.yf.exam.modules.user.exam.entity.UserExam; +import org.apache.ibatis.annotations.Param; + +/** +*

+* 考试记录Mapper +*

+* +* @author 聪明笨狗 +* @since 2020-09-21 15:13 +*/ +public interface UserExamMapper extends BaseMapper { + + /** + * 我的考试分页 + * @param page + * @param query + * @return + */ + IPage paging(Page page, @Param("query") UserExamReqDTO query); + +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/exam/service/UserExamService.java b/exam-api/src/main/java/com/yf/exam/modules/user/exam/service/UserExamService.java new file mode 100644 index 0000000..d1fbf72 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/exam/service/UserExamService.java @@ -0,0 +1,43 @@ +package com.yf.exam.modules.user.exam.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.user.exam.dto.request.UserExamReqDTO; +import com.yf.exam.modules.user.exam.dto.response.UserExamRespDTO; +import com.yf.exam.modules.user.exam.entity.UserExam; + +/** +*

+* 考试记录业务类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-21 15:13 +*/ +public interface UserExamService extends IService { + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage paging(PagingReqDTO reqDTO); + + /** + * 分页查询数据 + * @param reqDTO + * @return + */ + IPage myPaging(PagingReqDTO reqDTO); + + + /** + * 考试完成后加入成绩 + * @param userId + * @param examId + * @param score + * @param passed + */ + void joinResult(String userId, String examId, Integer score, boolean passed); +} diff --git a/exam-api/src/main/java/com/yf/exam/modules/user/exam/service/impl/UserExamServiceImpl.java b/exam-api/src/main/java/com/yf/exam/modules/user/exam/service/impl/UserExamServiceImpl.java new file mode 100644 index 0000000..226eb84 --- /dev/null +++ b/exam-api/src/main/java/com/yf/exam/modules/user/exam/service/impl/UserExamServiceImpl.java @@ -0,0 +1,88 @@ +package com.yf.exam.modules.user.exam.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yf.exam.core.api.dto.PagingReqDTO; +import com.yf.exam.modules.user.UserUtils; +import com.yf.exam.modules.user.exam.dto.request.UserExamReqDTO; +import com.yf.exam.modules.user.exam.dto.response.UserExamRespDTO; +import com.yf.exam.modules.user.exam.entity.UserExam; +import com.yf.exam.modules.user.exam.mapper.UserExamMapper; +import com.yf.exam.modules.user.exam.service.UserExamService; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** +*

+* 考试记录业务实现类 +*

+* +* @author 聪明笨狗 +* @since 2020-09-21 15:13 +*/ +@Service +public class UserExamServiceImpl extends ServiceImpl implements UserExamService { + + @Override + public IPage paging(PagingReqDTO reqDTO) { + + //转换结果 + IPage pageData = baseMapper.paging(reqDTO.toPage(), reqDTO.getParams()); + return pageData; + } + + @Override + public IPage myPaging(PagingReqDTO reqDTO) { + + UserExamReqDTO params = reqDTO.getParams(); + + + if(params==null){ + params = new UserExamReqDTO(); + } + + params.setUserId(UserUtils.getUserId()); + + + //转换结果 + IPage pageData = baseMapper.paging(reqDTO.toPage(), params); + return pageData; + } + + @Override + public void joinResult(String userId, String examId, Integer score, boolean passed) { + + //查询条件 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(UserExam::getUserId, userId) + .eq(UserExam::getExamId, examId); + + UserExam record = this.getOne(wrapper, false); + if(record == null){ + record = new UserExam(); + record.setCreateTime(new Date()); + record.setUpdateTime(new Date()); + record.setUserId(userId); + record.setExamId(examId); + record.setMaxScore(score); + record.setPassed(passed); + this.save(record); + return; + } + + // 修复低分数不加入统计问题 + record.setTryCount(record.getTryCount()+1); + record.setUpdateTime(new Date()); + + if(record.getMaxScore() < score){ + record.setMaxScore(score); + record.setPassed(passed); + } + + this.updateById(record); + + + } +} diff --git a/exam-api/src/main/resources/application-dev.yml b/exam-api/src/main/resources/application-dev.yml new file mode 100644 index 0000000..2c1344a --- /dev/null +++ b/exam-api/src/main/resources/application-dev.yml @@ -0,0 +1,71 @@ +# 开发环境配置文件 +spring: + # 数据库配置 + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/yf_exam_lite?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true + username: root + password: root@123 + # druid相关配置 + druid: + max-active: 5000 + initial-size: 20 + min-idle: 5 + async-init: true + # 监控统计 + filters: stat,wall + filter: + stat: + log-slow-sql: true + slow-sql-millis: 5000 + wall: + config: + create-table-allow: false + alter-table-allow: false + drop-table-allow: false + truncate-allow: false + + # 定时任务配置 + quartz: + # 数据库方式 + job-store-type: jdbc + # quartz 相关属性配置 + properties: + org: + quartz: + scheduler: + instanceName: eamScheduler + instanceId: AUTO + jobStore: + class: org.quartz.impl.jdbcjobstore.JobStoreTX + driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate + tablePrefix: QRTZ_ + isClustered: true + clusterCheckinInterval: 10000 + useProperties: false + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 10 + threadPriority: 5 + threadsInheritContextClassLoaderOfInitializingThread: true + +# 文件上传配置 +conf: + upload: + # 物理文件存储位置,以/结束,windows已正斜杠,如:d:/exam-upload/ + dir: /Users/van/Documents/work/upload/ + # 访问地址,注意不要去除/upload/file/,此节点为虚拟标识符 + # 如:http://localhost:8101/upload/file/exam.jpg,对应物理文件为:/data/upload/exam.jpg + url: http://localhost:8201/upload/file/ + # 允许上传的文件后缀 + allow-extensions: jpg,jpeg,png + +# 开启文档 +swagger: + enable: true + +logging: + level: + root: debug + path: logs/${spring.application.name}/ diff --git a/exam-api/src/main/resources/application-local.yml b/exam-api/src/main/resources/application-local.yml new file mode 100644 index 0000000..f5e82aa --- /dev/null +++ b/exam-api/src/main/resources/application-local.yml @@ -0,0 +1,85 @@ +# 独立配置文件,可以拿到jar外面跑 +spring: + application: + name: yf-exam-lite + profiles: + active: dev + main: + allow-bean-definition-overriding: true + # 数据库配置 + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/yf_exam_lite?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true + username: root + password: root + # druid相关配置 + druid: + max-active: 5000 + initial-size: 20 + min-idle: 5 + async-init: true + # 监控统计 + filters: stat,wall + filter: + stat: + log-slow-sql: true + slow-sql-millis: 5000 + wall: + config: + create-table-allow: false + alter-table-allow: false + drop-table-allow: false + truncate-allow: false + + # 定时任务配置 + quartz: + # 数据库方式 + job-store-type: jdbc + # quartz 相关属性配置 + properties: + org: + quartz: + scheduler: + instanceName: examScheduler + instanceId: AUTO + jobStore: + class: org.quartz.impl.jdbcjobstore.JobStoreTX + driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate + tablePrefix: QRTZ_ + isClustered: true + clusterCheckinInterval: 10000 + useProperties: false + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 10 + threadPriority: 5 + threadsInheritContextClassLoaderOfInitializingThread: true + +server: + port: 8101 + # 启用服务端压缩 + compression: + enabled: true + min-response-size: 10 + mime-types: application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css + +# 文件上传配置 +conf: + upload: + # 物理文件存储位置,以/结束,windows已正斜杠,如:d:/exam-upload/ + dir: /data/upload/ + # 访问地址,注意不要去除/upload/file/,此节点为虚拟标识符 + # 如:http://localhost:8101/upload/file/exam.jpg,对应物理文件为:/data/upload/exam.jpg + url: http://localhost:8101/upload/file/ + # 允许上传的文件后缀 + allow-extensions: jpg,jpeg,png + +# 开启文档 +swagger: + enable: true + +logging: + level: + root: debug + path: logs/${spring.application.name}/ diff --git a/exam-api/src/main/resources/application.yml b/exam-api/src/main/resources/application.yml new file mode 100644 index 0000000..839f049 --- /dev/null +++ b/exam-api/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + application: + name: yf-exam-lite + profiles: + active: dev + main: + allow-bean-definition-overriding: true +server: + port: 8101 + # 启用服务端压缩 + compression: + enabled: true + min-response-size: 10 + mime-types: application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css + diff --git a/exam-api/src/main/resources/mapper/exam/ExamDepartMapper.xml b/exam-api/src/main/resources/mapper/exam/ExamDepartMapper.xml new file mode 100644 index 0000000..7a9a90b --- /dev/null +++ b/exam-api/src/main/resources/mapper/exam/ExamDepartMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + `id`,`exam_id`,`depart_id` + + + diff --git a/exam-api/src/main/resources/mapper/exam/ExamMapper.xml b/exam-api/src/main/resources/mapper/exam/ExamMapper.xml new file mode 100644 index 0000000..8203a3b --- /dev/null +++ b/exam-api/src/main/resources/mapper/exam/ExamMapper.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + `id`,`title`,`content`,`open_type`,`join_type`,`level`,`state`,`time_limit`,`start_time`,`end_time`,`create_time`,`update_time`,`total_score`,`total_time`,`qualify_score` + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exam-api/src/main/resources/mapper/exam/ExamRepoMapper.xml b/exam-api/src/main/resources/mapper/exam/ExamRepoMapper.xml new file mode 100644 index 0000000..bc06691 --- /dev/null +++ b/exam-api/src/main/resources/mapper/exam/ExamRepoMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + `id`,`exam_id`,`repo_id`,`radio_count`,`radio_score`,`multi_count`,`multi_score`,`judge_count`,`judge_score` + + + + + + + + + + + diff --git a/exam-api/src/main/resources/mapper/paper/PaperMapper.xml b/exam-api/src/main/resources/mapper/paper/PaperMapper.xml new file mode 100644 index 0000000..8653ab1 --- /dev/null +++ b/exam-api/src/main/resources/mapper/paper/PaperMapper.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + `id`,`user_id`,`depart_id`,`exam_id`,`title`,`total_time`,`user_time`,`total_score`,`qualify_score`,`obj_score`,`subj_score`,`user_score`,`has_saq`,`state`,`create_time`,`update_time`,`limit_time` + + + + + + + + + + diff --git a/exam-api/src/main/resources/mapper/paper/PaperQuAnswerMapper.xml b/exam-api/src/main/resources/mapper/paper/PaperQuAnswerMapper.xml new file mode 100644 index 0000000..bd4f89b --- /dev/null +++ b/exam-api/src/main/resources/mapper/paper/PaperQuAnswerMapper.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + `id`,`paper_id`,`answer_id`,`qu_id`,`is_right`,`checked`,`sort`,`abc` + + + + + + + + + + + + + diff --git a/exam-api/src/main/resources/mapper/paper/PaperQuMapper.xml b/exam-api/src/main/resources/mapper/paper/PaperQuMapper.xml new file mode 100644 index 0000000..0498b71 --- /dev/null +++ b/exam-api/src/main/resources/mapper/paper/PaperQuMapper.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + `id`,`paper_id`,`qu_id`,`qu_type`,`answered`,`answer`,`sort`,`score`,`actual_score`,`is_right` + + + + + + + + + + + + + + + + + + + diff --git a/exam-api/src/main/resources/mapper/qu/QuAnswerMapper.xml b/exam-api/src/main/resources/mapper/qu/QuAnswerMapper.xml new file mode 100644 index 0000000..a5cfd35 --- /dev/null +++ b/exam-api/src/main/resources/mapper/qu/QuAnswerMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + `id`,`qu_id`,`is_right`,`image`,`content`,`analysis` + + + diff --git a/exam-api/src/main/resources/mapper/qu/QuMapper.xml b/exam-api/src/main/resources/mapper/qu/QuMapper.xml new file mode 100644 index 0000000..20ed746 --- /dev/null +++ b/exam-api/src/main/resources/mapper/qu/QuMapper.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + `id`,`qu_type`,`level`,`image`,`content`,`create_time`,`update_time`,`remark`,`analysis` + + + + + + + + + + + + + + + + + + + + + + + + + + AND q.qu_type = #{query.quType} + + + AND po.repo_id IN + #{repoId} + + + AND q.content LIKE CONCAT('%',#{query.content},'%') + + + AND q.id NOT IN + + #{quId} + + + + + + + + + + + + diff --git a/exam-api/src/main/resources/mapper/qu/QuRepoMapper.xml b/exam-api/src/main/resources/mapper/qu/QuRepoMapper.xml new file mode 100644 index 0000000..3353d6c --- /dev/null +++ b/exam-api/src/main/resources/mapper/qu/QuRepoMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + `id`,`qu_id`,`repo_id`,`qu_type`,`sort` + + + + diff --git a/exam-api/src/main/resources/mapper/repo/RepoMapper.xml b/exam-api/src/main/resources/mapper/repo/RepoMapper.xml new file mode 100644 index 0000000..d026b31 --- /dev/null +++ b/exam-api/src/main/resources/mapper/repo/RepoMapper.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + `id`,`code`,`title`,`radio_count`,`multi_count`,`judge_count`,`remark`,`create_time`,`update_time` + + + + + + + + + + + + + diff --git a/exam-api/src/main/resources/mapper/sys/depart/SysDepartMapper.xml b/exam-api/src/main/resources/mapper/sys/depart/SysDepartMapper.xml new file mode 100644 index 0000000..2bae742 --- /dev/null +++ b/exam-api/src/main/resources/mapper/sys/depart/SysDepartMapper.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + `id`,`dept_type`,`parent_id`,`dept_name`,`dept_code`,`sort` + + + + + + + + + + + + diff --git a/exam-api/src/main/resources/mapper/sys/system/SysDictMapper.xml b/exam-api/src/main/resources/mapper/sys/system/SysDictMapper.xml new file mode 100644 index 0000000..194ff86 --- /dev/null +++ b/exam-api/src/main/resources/mapper/sys/system/SysDictMapper.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/exam-api/src/main/resources/mapper/sys/user/SysRoleMapper.xml b/exam-api/src/main/resources/mapper/sys/user/SysRoleMapper.xml new file mode 100644 index 0000000..f79fbf6 --- /dev/null +++ b/exam-api/src/main/resources/mapper/sys/user/SysRoleMapper.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + `id`,`role_name` + + + diff --git a/exam-api/src/main/resources/mapper/sys/user/SysUserMapper.xml b/exam-api/src/main/resources/mapper/sys/user/SysUserMapper.xml new file mode 100644 index 0000000..7d9c81f --- /dev/null +++ b/exam-api/src/main/resources/mapper/sys/user/SysUserMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + `id`,`user_name`,`real_name`,`password`,`salt`,`role_ids`,`depart_id`,`create_time`,`update_time`,`state` + + + diff --git a/exam-api/src/main/resources/mapper/sys/user/SysUserRoleMapper.xml b/exam-api/src/main/resources/mapper/sys/user/SysUserRoleMapper.xml new file mode 100644 index 0000000..f2ea833 --- /dev/null +++ b/exam-api/src/main/resources/mapper/sys/user/SysUserRoleMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + `id`,`user_id`,`role_id` + + + diff --git a/exam-api/src/main/resources/mapper/user/UserBookMapper.xml b/exam-api/src/main/resources/mapper/user/UserBookMapper.xml new file mode 100644 index 0000000..17d6775 --- /dev/null +++ b/exam-api/src/main/resources/mapper/user/UserBookMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + `id`,`exam_id`,`user_id`,`qu_id`,`create_time`,`update_time`,`wrong_count`,`title`,`sort` + + + diff --git a/exam-api/src/main/resources/mapper/user/UserExamMapper.xml b/exam-api/src/main/resources/mapper/user/UserExamMapper.xml new file mode 100644 index 0000000..d123ee7 --- /dev/null +++ b/exam-api/src/main/resources/mapper/user/UserExamMapper.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + `id`,`user_id`,`exam_id`,`try_count`,`max_score`,`passed`,`create_time`,`update_time` + + + + + + + + + + + diff --git a/exam-api/src/main/resources/static/favicon.png b/exam-api/src/main/resources/static/favicon.png new file mode 100644 index 0000000..01f150e Binary files /dev/null and b/exam-api/src/main/resources/static/favicon.png differ diff --git a/exam-api/src/main/resources/static/index.html b/exam-api/src/main/resources/static/index.html new file mode 100644 index 0000000..3862b9d --- /dev/null +++ b/exam-api/src/main/resources/static/index.html @@ -0,0 +1,14 @@ + + + + + + + + + 云帆考试培训系统 + + +
+ + diff --git a/exam-api/src/main/resources/static/readMe.txt b/exam-api/src/main/resources/static/readMe.txt new file mode 100644 index 0000000..a581872 --- /dev/null +++ b/exam-api/src/main/resources/static/readMe.txt @@ -0,0 +1,2 @@ +为了方便单文件运行,可以把前端打包文件dist目录的内容复制到本目录下面,这样就可以直接运行jar就包含前端了,不需要单独再部署前端了。 +访问地址为:http://localhost:8101 \ No newline at end of file diff --git a/exam-api/src/main/resources/static/static/fonts/element-icons.535877f5.woff b/exam-api/src/main/resources/static/static/fonts/element-icons.535877f5.woff new file mode 100644 index 0000000..02b9a25 Binary files /dev/null and b/exam-api/src/main/resources/static/static/fonts/element-icons.535877f5.woff differ diff --git a/exam-api/src/main/resources/static/static/fonts/element-icons.732389de.ttf b/exam-api/src/main/resources/static/static/fonts/element-icons.732389de.ttf new file mode 100644 index 0000000..91b74de Binary files /dev/null and b/exam-api/src/main/resources/static/static/fonts/element-icons.732389de.ttf differ diff --git a/exam-api/src/main/resources/static/static/img/401.089007e7.gif b/exam-api/src/main/resources/static/static/img/401.089007e7.gif new file mode 100644 index 0000000..cd6e0d9 Binary files /dev/null and b/exam-api/src/main/resources/static/static/img/401.089007e7.gif differ diff --git a/exam-api/src/main/resources/static/static/img/404.a57b6f31.png b/exam-api/src/main/resources/static/static/img/404.a57b6f31.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/exam-api/src/main/resources/static/static/img/404.a57b6f31.png differ diff --git a/exam-api/src/main/resources/static/static/img/404_cloud.0f4bc32b.png b/exam-api/src/main/resources/static/static/img/404_cloud.0f4bc32b.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/exam-api/src/main/resources/static/static/img/404_cloud.0f4bc32b.png differ diff --git a/exam-api/src/main/resources/static/static/img/contact.22828125.png b/exam-api/src/main/resources/static/static/img/contact.22828125.png new file mode 100644 index 0000000..ce3e761 Binary files /dev/null and b/exam-api/src/main/resources/static/static/img/contact.22828125.png differ diff --git a/exam-api/src/main/resources/static/static/img/h5.ac62244a.png b/exam-api/src/main/resources/static/static/img/h5.ac62244a.png new file mode 100644 index 0000000..41478fd Binary files /dev/null and b/exam-api/src/main/resources/static/static/img/h5.ac62244a.png differ diff --git a/exam-api/src/main/resources/static/static/img/login-bg.5825f033.svg b/exam-api/src/main/resources/static/static/img/login-bg.5825f033.svg new file mode 100644 index 0000000..89c2597 --- /dev/null +++ b/exam-api/src/main/resources/static/static/img/login-bg.5825f033.svg @@ -0,0 +1,69 @@ + + + + Group 21 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/exam-api/src/main/resources/static/static/img/mp.1a3d1b7d.jpg b/exam-api/src/main/resources/static/static/img/mp.1a3d1b7d.jpg new file mode 100644 index 0000000..db5c422 Binary files /dev/null and b/exam-api/src/main/resources/static/static/img/mp.1a3d1b7d.jpg differ diff --git a/exam-api/src/main/resources/static/static/js/0.js b/exam-api/src/main/resources/static/static/js/0.js new file mode 100644 index 0000000..b1c22bd --- /dev/null +++ b/exam-api/src/main/resources/static/static/js/0.js @@ -0,0 +1,219 @@ +(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[0],{ + +/***/ "./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/FileUpload/index.vue?vue&type=script&lang=js&": +/*!*************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js??ref--13-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/FileUpload/index.vue?vue&type=script&lang=js& ***! + \*************************************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _local__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./local */ \"./src/components/FileUpload/local.vue\");\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'FileUpload',\n components: {\n FileUploadLocal: _local__WEBPACK_IMPORTED_MODULE_0__[\"default\"]\n },\n props: {\n value: String,\n accept: {\n type: String,\n default: '*'\n },\n tips: String,\n listType: {\n type: String,\n default: 'picture'\n }\n },\n data: function data() {\n return {\n fileUrl: ''\n };\n },\n watch: {\n // 检测查询变化\n value: {\n handler: function handler() {\n this.fillValue();\n }\n },\n // 检测查询变化\n fileUrl: {\n handler: function handler() {\n this.$emit('input', this.fileUrl);\n }\n }\n },\n mounted: function mounted() {},\n created: function created() {\n this.fillValue();\n },\n methods: {\n fillValue: function fillValue() {\n this.fileUrl = this.value;\n }\n }\n});\n\n//# sourceURL=webpack:///./src/components/FileUpload/index.vue?./node_modules/cache-loader/dist/cjs.js??ref--13-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + +/***/ "./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/FileUpload/local.vue?vue&type=script&lang=js&": +/*!*************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js??ref--13-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/FileUpload/local.vue?vue&type=script&lang=js& ***! + \*************************************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var core_js_modules_es6_number_constructor__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core-js/modules/es6.number.constructor */ \"./node_modules/core-js/modules/es6.number.constructor.js\");\n/* harmony import */ var core_js_modules_es6_number_constructor__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es6_number_constructor__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _utils_auth__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @/utils/auth */ \"./src/utils/auth.js\");\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'FileUploadLocal',\n props: {\n value: String,\n accept: String,\n tips: String,\n listType: String,\n limit: {\n type: Number,\n default: 1\n }\n },\n data: function data() {\n return {\n server: \"\".concat(\"\", \"/common/api/file/upload\"),\n fileList: [],\n fileUrl: '',\n header: {}\n };\n },\n watch: {\n // 检测查询变化\n value: {\n handler: function handler() {\n this.fillValue();\n }\n }\n },\n created: function created() {\n this.fillValue();\n this.header = {\n token: Object(_utils_auth__WEBPACK_IMPORTED_MODULE_1__[\"getToken\"])()\n };\n },\n methods: {\n fillValue: function fillValue() {\n this.fileList = [];\n this.fileUrl = this.value;\n\n if (this.fileUrl) {\n this.fileList = [{\n name: this.fileUrl,\n url: this.fileUrl\n }];\n }\n },\n // 文件超出个数限制时的钩子\n handleExceed: function handleExceed() {\n this.$message.warning(\"\\u6BCF\\u6B21\\u53EA\\u80FD\\u4E0A\\u4F20 \".concat(this.limit, \" \\u4E2A\\u6587\\u4EF6\"));\n },\n // 删除文件之前的钩子\n beforeRemove: function beforeRemove() {\n return this.$confirm(\"\\u786E\\u5B9A\\u79FB\\u9664\\u6587\\u4EF6\\u5417\\uFF1F\");\n },\n // 文件列表移除文件时的钩子\n handleRemove: function handleRemove() {\n this.$emit('input', '');\n this.fileList = [];\n },\n // 文件上传成功时的钩子\n handleSuccess: function handleSuccess(response) {\n if (response.code === 1) {\n this.$message({\n type: 'error',\n message: response.msg\n });\n this.fileList = [];\n return;\n }\n\n this.$emit('input', response.data.url);\n }\n }\n});\n\n//# sourceURL=webpack:///./src/components/FileUpload/local.vue?./node_modules/cache-loader/dist/cjs.js??ref--13-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + +/***/ "./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/qu/qu/form.vue?vue&type=script&lang=js&": +/*!**************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js??ref--13-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/views/qu/qu/form.vue?vue&type=script&lang=js& ***! + \**************************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core-js/modules/web.dom.iterable */ \"./node_modules/core-js/modules/web.dom.iterable.js\");\n/* harmony import */ var core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _api_qu_qu__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @/api/qu/qu */ \"./src/api/qu/qu.js\");\n/* harmony import */ var _components_RepoSelect__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @/components/RepoSelect */ \"./src/components/RepoSelect/index.vue\");\n/* harmony import */ var _components_FileUpload__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @/components/FileUpload */ \"./src/components/FileUpload/index.vue\");\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'QuDetail',\n components: {\n FileUpload: _components_FileUpload__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n RepoSelect: _components_RepoSelect__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n },\n data: function data() {\n return {\n quTypeDisabled: false,\n itemImage: true,\n levels: [{\n value: 1,\n label: '普通'\n }, {\n value: 2,\n label: '较难'\n }],\n quTypes: [{\n value: 1,\n label: '单选题'\n }, {\n value: 2,\n label: '多选题'\n }, {\n value: 3,\n label: '判断题'\n }],\n postForm: {\n repoIds: [],\n tagList: [],\n answerList: []\n },\n rules: {\n content: [{\n required: true,\n message: '题目内容不能为空!'\n }],\n quType: [{\n required: true,\n message: '题目类型不能为空!'\n }],\n level: [{\n required: true,\n message: '必须选择难度等级!'\n }],\n repoIds: [{\n required: true,\n message: '至少要选择一个题库!'\n }]\n }\n };\n },\n created: function created() {\n var id = this.$route.params.id;\n\n if (typeof id !== 'undefined') {\n this.quTypeDisabled = true;\n this.fetchData(id);\n }\n },\n methods: {\n handleTypeChange: function handleTypeChange(v) {\n this.postForm.answerList = [];\n\n if (v === 3) {\n this.postForm.answerList.push({\n isRight: true,\n content: '正确',\n analysis: ''\n });\n this.postForm.answerList.push({\n isRight: false,\n content: '错误',\n analysis: ''\n });\n }\n\n if (v === 1 || v === 2) {\n this.postForm.answerList.push({\n isRight: false,\n content: '',\n analysis: ''\n });\n this.postForm.answerList.push({\n isRight: false,\n content: '',\n analysis: ''\n });\n this.postForm.answerList.push({\n isRight: false,\n content: '',\n analysis: ''\n });\n this.postForm.answerList.push({\n isRight: false,\n content: '',\n analysis: ''\n });\n }\n },\n // 添加子项\n handleAdd: function handleAdd() {\n this.postForm.answerList.push({\n isRight: false,\n content: '',\n analysis: ''\n });\n },\n removeItem: function removeItem(index) {\n this.postForm.answerList.splice(index, 1);\n },\n fetchData: function fetchData(id) {\n var _this = this;\n\n Object(_api_qu_qu__WEBPACK_IMPORTED_MODULE_1__[\"fetchDetail\"])(id).then(function (response) {\n _this.postForm = response.data;\n });\n },\n submitForm: function submitForm() {\n var _this2 = this;\n\n console.log(JSON.stringify(this.postForm));\n var rightCount = 0;\n this.postForm.answerList.forEach(function (item) {\n if (item.isRight) {\n rightCount += 1;\n }\n });\n\n if (this.postForm.quType === 1) {\n if (rightCount !== 1) {\n this.$message({\n message: '单选题答案只能有一个',\n type: 'warning'\n });\n return;\n }\n }\n\n if (this.postForm.quType === 2) {\n if (rightCount < 2) {\n this.$message({\n message: '多选题至少要有两个正确答案!',\n type: 'warning'\n });\n return;\n }\n }\n\n if (this.postForm.quType === 3) {\n if (rightCount !== 1) {\n this.$message({\n message: '判断题只能有一个正确项!',\n type: 'warning'\n });\n return;\n }\n }\n\n this.$refs.postForm.validate(function (valid) {\n if (!valid) {\n return;\n }\n\n Object(_api_qu_qu__WEBPACK_IMPORTED_MODULE_1__[\"saveData\"])(_this2.postForm).then(function (response) {\n _this2.postForm = response.data;\n\n _this2.$notify({\n title: '成功',\n message: '试题保存成功!',\n type: 'success',\n duration: 2000\n });\n\n _this2.$router.push({\n name: 'ListQu'\n });\n });\n });\n },\n onCancel: function onCancel() {\n this.$router.push({\n name: 'ListQu'\n });\n }\n }\n});\n\n//# sourceURL=webpack:///./src/views/qu/qu/form.vue?./node_modules/cache-loader/dist/cjs.js??ref--13-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + +/***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/FileUpload/index.vue?vue&type=template&id=211f81e0&": +/*!*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"9323b05c-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/FileUpload/index.vue?vue&type=template&id=211f81e0& ***! + \*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function () {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n [\n _c(\"file-upload-local\", {\n attrs: {\n accept: _vm.accept,\n tips: _vm.tips,\n \"list-type\": _vm.listType,\n },\n model: {\n value: _vm.fileUrl,\n callback: function ($$v) {\n _vm.fileUrl = $$v\n },\n expression: \"fileUrl\",\n },\n }),\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./src/components/FileUpload/index.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%229323b05c-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + +/***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/FileUpload/local.vue?vue&type=template&id=5087fdae&": +/*!*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"9323b05c-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/FileUpload/local.vue?vue&type=template&id=5087fdae& ***! + \*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function () {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"content\" },\n [\n _c(\n \"el-upload\",\n {\n attrs: {\n action: _vm.server,\n accept: _vm.accept,\n \"before-remove\": _vm.beforeRemove,\n \"on-remove\": _vm.handleRemove,\n \"on-success\": _vm.handleSuccess,\n \"on-exceed\": _vm.handleExceed,\n drag: _vm.listType !== \"picture\",\n limit: _vm.limit,\n headers: _vm.header,\n \"file-list\": _vm.fileList,\n \"list-type\": _vm.listType,\n },\n model: {\n value: _vm.fileUrl,\n callback: function ($$v) {\n _vm.fileUrl = $$v\n },\n expression: \"fileUrl\",\n },\n },\n [\n _vm.listType === \"picture\"\n ? _c(\"el-button\", { attrs: { size: \"small\", type: \"primary\" } }, [\n _vm._v(\"点击上传\"),\n ])\n : _vm._e(),\n _vm.listType !== \"picture\"\n ? _c(\"i\", { staticClass: \"el-icon-upload\" })\n : _vm._e(),\n _vm.listType !== \"picture\"\n ? _c(\"div\", { staticClass: \"el-upload__text\" }, [\n _vm._v(\" 将文件拖到此处,或 \"),\n _c(\"em\", [_vm._v(\"点击上传\")]),\n ])\n : _vm._e(),\n _vm.tips\n ? _c(\n \"div\",\n {\n staticClass: \"el-upload__tip\",\n attrs: { slot: \"tip\" },\n slot: \"tip\",\n },\n [_vm._v(_vm._s(_vm.tips))]\n )\n : _vm._e(),\n ],\n 1\n ),\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./src/components/FileUpload/local.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%229323b05c-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + +/***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/qu/qu/form.vue?vue&type=template&id=4fe7c07e&scoped=true&": +/*!**********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"9323b05c-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/views/qu/qu/form.vue?vue&type=template&id=4fe7c07e&scoped=true& ***! + \**********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function () {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"app-container\" },\n [\n _c(\n \"el-form\",\n {\n ref: \"postForm\",\n attrs: {\n model: _vm.postForm,\n rules: _vm.rules,\n \"label-position\": \"left\",\n \"label-width\": \"150px\",\n },\n },\n [\n _c(\n \"el-card\",\n [\n _c(\n \"el-form-item\",\n { attrs: { label: \"题目类型 \", prop: \"quType\" } },\n [\n _c(\n \"el-select\",\n {\n staticClass: \"filter-item\",\n attrs: { disabled: _vm.quTypeDisabled },\n on: { change: _vm.handleTypeChange },\n model: {\n value: _vm.postForm.quType,\n callback: function ($$v) {\n _vm.$set(_vm.postForm, \"quType\", $$v)\n },\n expression: \"postForm.quType\",\n },\n },\n _vm._l(_vm.quTypes, function (item) {\n return _c(\"el-option\", {\n key: item.value,\n attrs: { label: item.label, value: item.value },\n })\n }),\n 1\n ),\n ],\n 1\n ),\n _c(\n \"el-form-item\",\n { attrs: { label: \"难度等级 \", prop: \"level\" } },\n [\n _c(\n \"el-select\",\n {\n staticClass: \"filter-item\",\n model: {\n value: _vm.postForm.level,\n callback: function ($$v) {\n _vm.$set(_vm.postForm, \"level\", $$v)\n },\n expression: \"postForm.level\",\n },\n },\n _vm._l(_vm.levels, function (item) {\n return _c(\"el-option\", {\n key: item.value,\n attrs: { label: item.label, value: item.value },\n })\n }),\n 1\n ),\n ],\n 1\n ),\n _c(\n \"el-form-item\",\n { attrs: { label: \"归属题库\", prop: \"repoIds\" } },\n [\n _c(\"repo-select\", {\n attrs: { multi: true },\n model: {\n value: _vm.postForm.repoIds,\n callback: function ($$v) {\n _vm.$set(_vm.postForm, \"repoIds\", $$v)\n },\n expression: \"postForm.repoIds\",\n },\n }),\n ],\n 1\n ),\n _c(\n \"el-form-item\",\n { attrs: { label: \"题目内容\", prop: \"content\" } },\n [\n _c(\"el-input\", {\n attrs: { type: \"textarea\" },\n model: {\n value: _vm.postForm.content,\n callback: function ($$v) {\n _vm.$set(_vm.postForm, \"content\", $$v)\n },\n expression: \"postForm.content\",\n },\n }),\n ],\n 1\n ),\n _c(\n \"el-form-item\",\n { attrs: { label: \"试题图片\" } },\n [\n _c(\"file-upload\", {\n attrs: { accept: \".jpg,.jepg,.png\" },\n model: {\n value: _vm.postForm.image,\n callback: function ($$v) {\n _vm.$set(_vm.postForm, \"image\", $$v)\n },\n expression: \"postForm.image\",\n },\n }),\n ],\n 1\n ),\n _c(\n \"el-form-item\",\n { attrs: { label: \"整题解析\", prop: \"oriPrice\" } },\n [\n _c(\"el-input\", {\n attrs: { precision: 1, max: 999999, type: \"textarea\" },\n model: {\n value: _vm.postForm.analysis,\n callback: function ($$v) {\n _vm.$set(_vm.postForm, \"analysis\", $$v)\n },\n expression: \"postForm.analysis\",\n },\n }),\n ],\n 1\n ),\n ],\n 1\n ),\n _vm.postForm.quType !== 4\n ? _c(\n \"div\",\n {\n staticClass: \"filter-container\",\n staticStyle: { \"margin-top\": \"25px\" },\n },\n [\n _c(\n \"el-button\",\n {\n staticClass: \"filter-item\",\n attrs: {\n type: \"primary\",\n icon: \"el-icon-plus\",\n size: \"small\",\n plain: \"\",\n },\n on: { click: _vm.handleAdd },\n },\n [_vm._v(\" 添加 \")]\n ),\n _c(\n \"el-table\",\n {\n staticStyle: { width: \"100%\" },\n attrs: { data: _vm.postForm.answerList, border: true },\n },\n [\n _c(\"el-table-column\", {\n attrs: {\n label: \"是否答案\",\n width: \"120\",\n align: \"center\",\n },\n scopedSlots: _vm._u(\n [\n {\n key: \"default\",\n fn: function (scope) {\n return [\n _c(\n \"el-checkbox\",\n {\n model: {\n value: scope.row.isRight,\n callback: function ($$v) {\n _vm.$set(scope.row, \"isRight\", $$v)\n },\n expression: \"scope.row.isRight\",\n },\n },\n [_vm._v(\"答案\")]\n ),\n ]\n },\n },\n ],\n null,\n false,\n 1650073960\n ),\n }),\n _vm.itemImage\n ? _c(\"el-table-column\", {\n attrs: {\n label: \"选项图片\",\n width: \"120px\",\n align: \"center\",\n },\n scopedSlots: _vm._u(\n [\n {\n key: \"default\",\n fn: function (scope) {\n return [\n _c(\"file-upload\", {\n attrs: { accept: \".jpg,.jepg,.png\" },\n model: {\n value: scope.row.image,\n callback: function ($$v) {\n _vm.$set(scope.row, \"image\", $$v)\n },\n expression: \"scope.row.image\",\n },\n }),\n ]\n },\n },\n ],\n null,\n false,\n 2051426284\n ),\n })\n : _vm._e(),\n _c(\"el-table-column\", {\n attrs: { label: \"答案内容\" },\n scopedSlots: _vm._u(\n [\n {\n key: \"default\",\n fn: function (scope) {\n return [\n _c(\"el-input\", {\n attrs: { type: \"textarea\" },\n model: {\n value: scope.row.content,\n callback: function ($$v) {\n _vm.$set(scope.row, \"content\", $$v)\n },\n expression: \"scope.row.content\",\n },\n }),\n ]\n },\n },\n ],\n null,\n false,\n 924406712\n ),\n }),\n _c(\"el-table-column\", {\n attrs: { label: \"答案解析\" },\n scopedSlots: _vm._u(\n [\n {\n key: \"default\",\n fn: function (scope) {\n return [\n _c(\"el-input\", {\n attrs: { type: \"textarea\" },\n model: {\n value: scope.row.analysis,\n callback: function ($$v) {\n _vm.$set(scope.row, \"analysis\", $$v)\n },\n expression: \"scope.row.analysis\",\n },\n }),\n ]\n },\n },\n ],\n null,\n false,\n 3792987939\n ),\n }),\n _c(\"el-table-column\", {\n attrs: {\n label: \"操作\",\n align: \"center\",\n width: \"100px\",\n },\n scopedSlots: _vm._u(\n [\n {\n key: \"default\",\n fn: function (scope) {\n return [\n _c(\"el-button\", {\n attrs: {\n type: \"danger\",\n icon: \"el-icon-delete\",\n circle: \"\",\n },\n on: {\n click: function ($event) {\n return _vm.removeItem(scope.$index)\n },\n },\n }),\n ]\n },\n },\n ],\n null,\n false,\n 1518468532\n ),\n }),\n ],\n 1\n ),\n ],\n 1\n )\n : _vm._e(),\n _c(\n \"div\",\n { staticStyle: { \"margin-top\": \"20px\" } },\n [\n _c(\n \"el-button\",\n { attrs: { type: \"primary\" }, on: { click: _vm.submitForm } },\n [_vm._v(\"保存\")]\n ),\n _c(\n \"el-button\",\n { attrs: { type: \"info\" }, on: { click: _vm.onCancel } },\n [_vm._v(\"返回\")]\n ),\n ],\n 1\n ),\n ],\n 1\n ),\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./src/views/qu/qu/form.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%229323b05c-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + +/***/ "./node_modules/core-js/modules/es6.array.find.js": +/*!********************************************************!*\ + !*** ./node_modules/core-js/modules/es6.array.find.js ***! + \********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n// 22.1.3.8 Array.prototype.find(predicate, thisArg = undefined)\nvar $export = __webpack_require__(/*! ./_export */ \"./node_modules/core-js/modules/_export.js\");\nvar $find = __webpack_require__(/*! ./_array-methods */ \"./node_modules/core-js/modules/_array-methods.js\")(5);\nvar KEY = 'find';\nvar forced = true;\n// Shouldn't skip holes\nif (KEY in []) Array(1)[KEY](function () { forced = false; });\n$export($export.P + $export.F * forced, 'Array', {\n find: function find(callbackfn /* , that = undefined */) {\n return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);\n }\n});\n__webpack_require__(/*! ./_add-to-unscopables */ \"./node_modules/core-js/modules/_add-to-unscopables.js\")(KEY);\n\n\n//# sourceURL=webpack:///./node_modules/core-js/modules/es6.array.find.js?"); + +/***/ }), + +/***/ "./src/api/qu/qu.js": +/*!**************************!*\ + !*** ./src/api/qu/qu.js ***! + \**************************/ +/*! exports provided: fetchDetail, saveData, exportExcel, importTemplate, importExcel */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fetchDetail\", function() { return fetchDetail; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saveData\", function() { return saveData; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"exportExcel\", function() { return exportExcel; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"importTemplate\", function() { return importTemplate; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"importExcel\", function() { return importExcel; });\n/* harmony import */ var _utils_request__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @/utils/request */ \"./src/utils/request.js\");\n\n/**\n * 题库详情\n * @param data\n */\n\nfunction fetchDetail(id) {\n return Object(_utils_request__WEBPACK_IMPORTED_MODULE_0__[\"post\"])('/exam/api/qu/qu/detail', {\n id: id\n });\n}\n/**\n * 保存题库\n * @param data\n */\n\nfunction saveData(data) {\n return Object(_utils_request__WEBPACK_IMPORTED_MODULE_0__[\"post\"])('/exam/api/qu/qu/save', data);\n}\n/**\n * 导出\n * @param data\n */\n\nfunction exportExcel(data) {\n return Object(_utils_request__WEBPACK_IMPORTED_MODULE_0__[\"download\"])('/exam/api/qu/qu/export', data, '导出的数据.xlsx');\n}\n/**\n * 导入模板\n * @param data\n */\n\nfunction importTemplate() {\n return Object(_utils_request__WEBPACK_IMPORTED_MODULE_0__[\"download\"])('/exam/api/qu/qu/import/template', {}, 'qu-import-template.xlsx');\n}\n/**\n * 导出\n * @param data\n */\n\nfunction importExcel(file) {\n return Object(_utils_request__WEBPACK_IMPORTED_MODULE_0__[\"upload\"])('/exam/api/qu/qu/import', file);\n}\n\n//# sourceURL=webpack:///./src/api/qu/qu.js?"); + +/***/ }), + +/***/ "./src/api/qu/repo.js": +/*!****************************!*\ + !*** ./src/api/qu/repo.js ***! + \****************************/ +/*! exports provided: fetchDetail, saveData, fetchPaging, batchAction */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fetchDetail\", function() { return fetchDetail; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saveData\", function() { return saveData; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fetchPaging\", function() { return fetchPaging; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"batchAction\", function() { return batchAction; });\n/* harmony import */ var _utils_request__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @/utils/request */ \"./src/utils/request.js\");\n\n/**\n * 题库详情\n * @param data\n */\n\nfunction fetchDetail(data) {\n return Object(_utils_request__WEBPACK_IMPORTED_MODULE_0__[\"post\"])('/exam/api/repo/detail', data);\n}\n/**\n * 保存题库\n * @param data\n */\n\nfunction saveData(data) {\n return Object(_utils_request__WEBPACK_IMPORTED_MODULE_0__[\"post\"])('/exam/api/repo/save', data);\n}\n/**\n * 保存题库\n * @param data\n */\n\nfunction fetchPaging(data) {\n return Object(_utils_request__WEBPACK_IMPORTED_MODULE_0__[\"post\"])('/exam/api/repo/paging', data);\n}\n/**\n * 题库批量操作\n * @param data\n */\n\nfunction batchAction(data) {\n return Object(_utils_request__WEBPACK_IMPORTED_MODULE_0__[\"post\"])('/exam/api/repo/batch-action', data);\n}\n\n//# sourceURL=webpack:///./src/api/qu/repo.js?"); + +/***/ }), + +/***/ "./src/components/FileUpload/index.vue": +/*!*********************************************!*\ + !*** ./src/components/FileUpload/index.vue ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index_vue_vue_type_template_id_211f81e0___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index.vue?vue&type=template&id=211f81e0& */ \"./src/components/FileUpload/index.vue?vue&type=template&id=211f81e0&\");\n/* harmony import */ var _index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index.vue?vue&type=script&lang=js& */ \"./src/components/FileUpload/index.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(\n _index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _index_vue_vue_type_template_id_211f81e0___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _index_vue_vue_type_template_id_211f81e0___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/components/FileUpload/index.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/components/FileUpload/index.vue?"); + +/***/ }), + +/***/ "./src/components/FileUpload/index.vue?vue&type=script&lang=js&": +/*!**********************************************************************!*\ + !*** ./src/components/FileUpload/index.vue?vue&type=script&lang=js& ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../node_modules/babel-loader/lib!../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/FileUpload/index.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/components/FileUpload/index.vue?"); + +/***/ }), + +/***/ "./src/components/FileUpload/index.vue?vue&type=template&id=211f81e0&": +/*!****************************************************************************!*\ + !*** ./src/components/FileUpload/index.vue?vue&type=template&id=211f81e0& ***! + \****************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_211f81e0___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=template&id=211f81e0& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/FileUpload/index.vue?vue&type=template&id=211f81e0&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_211f81e0___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_211f81e0___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/components/FileUpload/index.vue?"); + +/***/ }), + +/***/ "./src/components/FileUpload/local.vue": +/*!*********************************************!*\ + !*** ./src/components/FileUpload/local.vue ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _local_vue_vue_type_template_id_5087fdae___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./local.vue?vue&type=template&id=5087fdae& */ \"./src/components/FileUpload/local.vue?vue&type=template&id=5087fdae&\");\n/* harmony import */ var _local_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./local.vue?vue&type=script&lang=js& */ \"./src/components/FileUpload/local.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(\n _local_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _local_vue_vue_type_template_id_5087fdae___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _local_vue_vue_type_template_id_5087fdae___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/components/FileUpload/local.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/components/FileUpload/local.vue?"); + +/***/ }), + +/***/ "./src/components/FileUpload/local.vue?vue&type=script&lang=js&": +/*!**********************************************************************!*\ + !*** ./src/components/FileUpload/local.vue?vue&type=script&lang=js& ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_local_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../node_modules/babel-loader/lib!../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../node_modules/vue-loader/lib??vue-loader-options!./local.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/FileUpload/local.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_local_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/components/FileUpload/local.vue?"); + +/***/ }), + +/***/ "./src/components/FileUpload/local.vue?vue&type=template&id=5087fdae&": +/*!****************************************************************************!*\ + !*** ./src/components/FileUpload/local.vue?vue&type=template&id=5087fdae& ***! + \****************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_local_vue_vue_type_template_id_5087fdae___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../node_modules/vue-loader/lib??vue-loader-options!./local.vue?vue&type=template&id=5087fdae& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/FileUpload/local.vue?vue&type=template&id=5087fdae&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_local_vue_vue_type_template_id_5087fdae___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_local_vue_vue_type_template_id_5087fdae___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/components/FileUpload/local.vue?"); + +/***/ }), + +/***/ "./src/views/qu/qu/form.vue": +/*!**********************************!*\ + !*** ./src/views/qu/qu/form.vue ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _form_vue_vue_type_template_id_4fe7c07e_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./form.vue?vue&type=template&id=4fe7c07e&scoped=true& */ \"./src/views/qu/qu/form.vue?vue&type=template&id=4fe7c07e&scoped=true&\");\n/* harmony import */ var _form_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./form.vue?vue&type=script&lang=js& */ \"./src/views/qu/qu/form.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(\n _form_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _form_vue_vue_type_template_id_4fe7c07e_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _form_vue_vue_type_template_id_4fe7c07e_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n \"4fe7c07e\",\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/views/qu/qu/form.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/views/qu/qu/form.vue?"); + +/***/ }), + +/***/ "./src/views/qu/qu/form.vue?vue&type=script&lang=js&": +/*!***********************************************************!*\ + !*** ./src/views/qu/qu/form.vue?vue&type=script&lang=js& ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_form_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../../node_modules/babel-loader/lib!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./form.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/qu/qu/form.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_form_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/views/qu/qu/form.vue?"); + +/***/ }), + +/***/ "./src/views/qu/qu/form.vue?vue&type=template&id=4fe7c07e&scoped=true&": +/*!*****************************************************************************!*\ + !*** ./src/views/qu/qu/form.vue?vue&type=template&id=4fe7c07e&scoped=true& ***! + \*****************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_form_vue_vue_type_template_id_4fe7c07e_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./form.vue?vue&type=template&id=4fe7c07e&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/qu/qu/form.vue?vue&type=template&id=4fe7c07e&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_form_vue_vue_type_template_id_4fe7c07e_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_form_vue_vue_type_template_id_4fe7c07e_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/views/qu/qu/form.vue?"); + +/***/ }) + +}]); \ No newline at end of file diff --git a/exam-api/src/main/resources/static/static/js/1.js b/exam-api/src/main/resources/static/static/js/1.js new file mode 100644 index 0000000..46609e8 --- /dev/null +++ b/exam-api/src/main/resources/static/static/js/1.js @@ -0,0 +1,133 @@ +(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],{ + +/***/ "./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/DepartTreeSelect/index.vue?vue&type=script&lang=js&": +/*!*******************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js??ref--13-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/DepartTreeSelect/index.vue?vue&type=script&lang=js& ***! + \*******************************************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'DepartTree',\n // 设置绑定参数\n model: {\n prop: 'value',\n event: 'selected'\n },\n props: {\n // 接收绑定参数\n value: String,\n // 输入框宽度\n width: String,\n // 选项数据\n options: {\n type: Array,\n required: true\n },\n // 输入框占位符\n placeholder: {\n type: String,\n required: false,\n default: '请选择'\n },\n // 树节点配置选项\n props: {\n type: Object,\n required: false,\n default: function _default() {\n return {\n parent: 'parentId',\n value: 'rowGuid',\n label: 'areaName',\n children: 'children'\n };\n }\n }\n },\n data: function data() {\n return {\n // 树状菜单显示状态\n showStatus: false,\n // 菜单宽度\n treeWidth: 'auto',\n // 输入框显示值\n labelModel: '',\n // 实际请求传值\n valueModel: '0'\n };\n },\n computed: {\n // 是否为树状结构数据\n dataType: function dataType() {\n var jsonStr = JSON.stringify(this.options);\n return jsonStr.indexOf(this.props.children) !== -1;\n },\n // 若非树状结构,则转化为树状结构数据\n data: function data() {\n return this.dataType ? this.options : this.switchTree();\n }\n },\n watch: {\n labelModel: function labelModel(val) {\n if (!val) {\n this.valueModel = '';\n }\n\n this.$refs.tree.filter(val);\n },\n value: function value(val) {\n this.labelModel = this.queryTree(this.data, val);\n }\n },\n created: function created() {\n var _this = this;\n\n // 检测输入框原有值并显示对应 label\n if (this.value) {\n this.labelModel = this.queryTree(this.data, this.value);\n } // 获取输入框宽度同步至树状菜单宽度\n\n\n this.$nextTick(function () {\n _this.treeWidth = \"\".concat((_this.width || _this.$refs.input.$refs.input.clientWidth) - 24, \"px\");\n });\n },\n methods: {\n // 单击节点\n onClickNode: function onClickNode(node) {\n this.labelModel = node[this.props.label];\n this.valueModel = node[this.props.value];\n this.onCloseTree();\n },\n // 偏平数组转化为树状层级结构\n switchTree: function switchTree() {\n return this.cleanChildren(this.buildTree(this.options, '0'));\n },\n // 隐藏树状菜单\n onCloseTree: function onCloseTree() {\n this.$refs.popover.showPopper = false;\n },\n // 显示时触发\n onShowPopover: function onShowPopover() {\n this.showStatus = true;\n this.$refs.tree.filter(false);\n },\n // 隐藏时触发\n onHidePopover: function onHidePopover() {\n this.showStatus = false;\n this.$emit('selected', this.valueModel);\n },\n // 树节点过滤方法\n filterNode: function filterNode(query, data) {\n if (!query) return true;\n return data[this.props.label].indexOf(query) !== -1;\n },\n // 搜索树状数据中的 ID\n queryTree: function queryTree(tree, id) {\n var stark = [];\n stark = stark.concat(tree);\n\n while (stark.length) {\n var temp = stark.shift();\n\n if (temp[this.props.children]) {\n stark = stark.concat(temp[this.props.children]);\n }\n\n if (temp[this.props.value] === id) {\n return temp[this.props.label];\n }\n }\n\n return '';\n },\n // 将一维的扁平数组转换为多层级对象\n buildTree: function buildTree(data) {\n var _this2 = this;\n\n var id = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '0';\n\n var fa = function fa(parentId) {\n var temp = [];\n\n for (var i = 0; i < data.length; i++) {\n var n = data[i];\n\n if (n[_this2.props.parent] === parentId) {\n n.children = fa(n.rowGuid);\n temp.push(n);\n }\n }\n\n return temp;\n };\n\n return fa(id);\n },\n // 清除空 children项\n cleanChildren: function cleanChildren(data) {\n var fa = function fa(list) {\n list.map(function (e) {\n if (e.children.length) {\n fa(e.children);\n } else {\n delete e.children;\n }\n\n return e;\n });\n return list;\n };\n\n return fa(data);\n }\n }\n});\n\n//# sourceURL=webpack:///./src/components/DepartTreeSelect/index.vue?./node_modules/cache-loader/dist/cjs.js??ref--13-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + +/***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/DepartTreeSelect/index.vue?vue&type=template&id=1392eafe&": +/*!***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"9323b05c-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/DepartTreeSelect/index.vue?vue&type=template&id=1392eafe& ***! + \***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function () {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"el-popover\",\n {\n ref: \"popover\",\n attrs: { placement: \"bottom-start\", trigger: \"click\" },\n on: { show: _vm.onShowPopover, hide: _vm.onHidePopover },\n },\n [\n _c(\"el-tree\", {\n ref: \"tree\",\n staticClass: \"select-tree\",\n style: \"min-width: \" + _vm.treeWidth,\n attrs: {\n data: _vm.data,\n props: _vm.props,\n \"expand-on-click-node\": false,\n \"filter-node-method\": _vm.filterNode,\n placeholder: \"选择部门\",\n \"check-strictly\": false,\n \"highlight-current\": \"\",\n \"default-expand-all\": \"\",\n },\n on: { \"node-click\": _vm.onClickNode },\n }),\n _c(\"el-input\", {\n ref: \"input\",\n class: { rotate: _vm.showStatus },\n style: \"width: \" + _vm.width + \"px\",\n attrs: {\n slot: \"reference\",\n placeholder: _vm.placeholder,\n clearable: \"\",\n \"suffix-icon\": \"el-icon-arrow-down\",\n },\n slot: \"reference\",\n model: {\n value: _vm.labelModel,\n callback: function ($$v) {\n _vm.labelModel = $$v\n },\n expression: \"labelModel\",\n },\n }),\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./src/components/DepartTreeSelect/index.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%229323b05c-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + +/***/ "./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/DepartTreeSelect/index.vue?vue&type=style&index=0&lang=css&": +/*!*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/css-loader/dist/cjs.js??ref--7-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--7-oneOf-1-2!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/DepartTreeSelect/index.vue?vue&type=style&index=0&lang=css& ***! + \*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \"\\n.el-input.el-input--suffix {\\n cursor: pointer;\\n overflow: hidden;\\n}\\n.el-input.el-input--suffix.rotate .el-input__suffix {\\n -webkit-transform: rotate(180deg);\\n transform: rotate(180deg);\\n}\\n.select-tree {\\n max-height: 350px;\\n overflow-y: scroll;\\n}\\n/* 菜单滚动条 */\\n.select-tree::-webkit-scrollbar {\\n z-index: 11;\\n width: 6px;\\n}\\n.select-tree::-webkit-scrollbar-track,\\n.select-tree::-webkit-scrollbar-corner {\\n background: #fff;\\n}\\n.select-tree::-webkit-scrollbar-thumb {\\n border-radius: 5px;\\n width: 6px;\\n background: #b4bccc;\\n}\\n.select-tree::-webkit-scrollbar-track-piece {\\n background: #fff;\\n width: 6px;\\n}\\n\", \"\"]);\n// Exports\nmodule.exports = exports;\n\n\n//# sourceURL=webpack:///./src/components/DepartTreeSelect/index.vue?./node_modules/css-loader/dist/cjs.js??ref--7-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--7-oneOf-1-2!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options"); + +/***/ }), + +/***/ "./node_modules/vue-style-loader/index.js?!./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/DepartTreeSelect/index.vue?vue&type=style&index=0&lang=css&": +/*!***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/vue-style-loader??ref--7-oneOf-1-0!./node_modules/css-loader/dist/cjs.js??ref--7-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--7-oneOf-1-2!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/DepartTreeSelect/index.vue?vue&type=style&index=0&lang=css& ***! + \***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("// style-loader: Adds some css to the DOM by adding a \"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/admin.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/agreement.svg": +/*!*************************************!*\ + !*** ./src/icons/svg/agreement.svg ***! + \*************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-agreement\",\n \"use\": \"icon-agreement-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/agreement.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/bug.svg": +/*!*******************************!*\ + !*** ./src/icons/svg/bug.svg ***! + \*******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-bug\",\n \"use\": \"icon-bug-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/bug.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/chart.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/chart.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-chart\",\n \"use\": \"icon-chart-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/chart.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/clipboard.svg": +/*!*************************************!*\ + !*** ./src/icons/svg/clipboard.svg ***! + \*************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-clipboard\",\n \"use\": \"icon-clipboard-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/clipboard.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/component.svg": +/*!*************************************!*\ + !*** ./src/icons/svg/component.svg ***! + \*************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-component\",\n \"use\": \"icon-component-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/component.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/configure.svg": +/*!*************************************!*\ + !*** ./src/icons/svg/configure.svg ***! + \*************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-configure\",\n \"use\": \"icon-configure-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/configure.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/dashboard.svg": +/*!*************************************!*\ + !*** ./src/icons/svg/dashboard.svg ***! + \*************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-dashboard\",\n \"use\": \"icon-dashboard-usage\",\n \"viewBox\": \"0 0 128 100\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/dashboard.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/documentation.svg": +/*!*****************************************!*\ + !*** ./src/icons/svg/documentation.svg ***! + \*****************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-documentation\",\n \"use\": \"icon-documentation-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/documentation.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/drag.svg": +/*!********************************!*\ + !*** ./src/icons/svg/drag.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-drag\",\n \"use\": \"icon-drag-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/drag.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/edit.svg": +/*!********************************!*\ + !*** ./src/icons/svg/edit.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-edit\",\n \"use\": \"icon-edit-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/edit.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/education.svg": +/*!*************************************!*\ + !*** ./src/icons/svg/education.svg ***! + \*************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-education\",\n \"use\": \"icon-education-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/education.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/email.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/email.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-email\",\n \"use\": \"icon-email-usage\",\n \"viewBox\": \"0 0 128 96\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/email.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/example.svg": +/*!***********************************!*\ + !*** ./src/icons/svg/example.svg ***! + \***********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-example\",\n \"use\": \"icon-example-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/example.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/excel.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/excel.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-excel\",\n \"use\": \"icon-excel-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/excel.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/exit-fullscreen.svg": +/*!*******************************************!*\ + !*** ./src/icons/svg/exit-fullscreen.svg ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-exit-fullscreen\",\n \"use\": \"icon-exit-fullscreen-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/exit-fullscreen.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/eye-open.svg": +/*!************************************!*\ + !*** ./src/icons/svg/eye-open.svg ***! + \************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-eye-open\",\n \"use\": \"icon-eye-open-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/eye-open.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/eye.svg": +/*!*******************************!*\ + !*** ./src/icons/svg/eye.svg ***! + \*******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-eye\",\n \"use\": \"icon-eye-usage\",\n \"viewBox\": \"0 0 128 64\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/eye.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/fire.svg": +/*!********************************!*\ + !*** ./src/icons/svg/fire.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-fire\",\n \"use\": \"icon-fire-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\\n \\n \\n\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/fire.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/form.svg": +/*!********************************!*\ + !*** ./src/icons/svg/form.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-form\",\n \"use\": \"icon-form-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/form.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/fullscreen.svg": +/*!**************************************!*\ + !*** ./src/icons/svg/fullscreen.svg ***! + \**************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-fullscreen\",\n \"use\": \"icon-fullscreen-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/fullscreen.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/guide.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/guide.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-guide\",\n \"use\": \"icon-guide-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/guide.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/hot.svg": +/*!*******************************!*\ + !*** ./src/icons/svg/hot.svg ***! + \*******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-hot\",\n \"use\": \"icon-hot-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/hot.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/icon.svg": +/*!********************************!*\ + !*** ./src/icons/svg/icon.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-icon\",\n \"use\": \"icon-icon-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/icon.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/international.svg": +/*!*****************************************!*\ + !*** ./src/icons/svg/international.svg ***! + \*****************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-international\",\n \"use\": \"icon-international-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/international.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/language.svg": +/*!************************************!*\ + !*** ./src/icons/svg/language.svg ***! + \************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-language\",\n \"use\": \"icon-language-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/language.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/link.svg": +/*!********************************!*\ + !*** ./src/icons/svg/link.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-link\",\n \"use\": \"icon-link-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/link.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/list.svg": +/*!********************************!*\ + !*** ./src/icons/svg/list.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-list\",\n \"use\": \"icon-list-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/list.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/lock.svg": +/*!********************************!*\ + !*** ./src/icons/svg/lock.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-lock\",\n \"use\": \"icon-lock-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/lock.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/log.svg": +/*!*******************************!*\ + !*** ./src/icons/svg/log.svg ***! + \*******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-log\",\n \"use\": \"icon-log-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/log.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/map.svg": +/*!*******************************!*\ + !*** ./src/icons/svg/map.svg ***! + \*******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-map\",\n \"use\": \"icon-map-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/map.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/message.svg": +/*!***********************************!*\ + !*** ./src/icons/svg/message.svg ***! + \***********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-message\",\n \"use\": \"icon-message-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/message.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/money.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/money.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-money\",\n \"use\": \"icon-money-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/money.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/nested.svg": +/*!**********************************!*\ + !*** ./src/icons/svg/nested.svg ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-nested\",\n \"use\": \"icon-nested-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/nested.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/notify.svg": +/*!**********************************!*\ + !*** ./src/icons/svg/notify.svg ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-notify\",\n \"use\": \"icon-notify-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\\n \\n \\n\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/notify.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/paper.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/paper.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-paper\",\n \"use\": \"icon-paper-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\\n \\n \\n \\n \\n \\n\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/paper.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/password.svg": +/*!************************************!*\ + !*** ./src/icons/svg/password.svg ***! + \************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-password\",\n \"use\": \"icon-password-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/password.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/pdf.svg": +/*!*******************************!*\ + !*** ./src/icons/svg/pdf.svg ***! + \*******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-pdf\",\n \"use\": \"icon-pdf-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/pdf.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/people.svg": +/*!**********************************!*\ + !*** ./src/icons/svg/people.svg ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-people\",\n \"use\": \"icon-people-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/people.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/peoples.svg": +/*!***********************************!*\ + !*** ./src/icons/svg/peoples.svg ***! + \***********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-peoples\",\n \"use\": \"icon-peoples-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/peoples.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/qq.svg": +/*!******************************!*\ + !*** ./src/icons/svg/qq.svg ***! + \******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-qq\",\n \"use\": \"icon-qq-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/qq.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/repo.svg": +/*!********************************!*\ + !*** ./src/icons/svg/repo.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-repo\",\n \"use\": \"icon-repo-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\\n \\n \\n\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/repo.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/results.svg": +/*!***********************************!*\ + !*** ./src/icons/svg/results.svg ***! + \***********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-results\",\n \"use\": \"icon-results-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/results.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/review.svg": +/*!**********************************!*\ + !*** ./src/icons/svg/review.svg ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-review\",\n \"use\": \"icon-review-usage\",\n \"viewBox\": \"0 0 1047 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/review.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/role.svg": +/*!********************************!*\ + !*** ./src/icons/svg/role.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-role\",\n \"use\": \"icon-role-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/role.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/search.svg": +/*!**********************************!*\ + !*** ./src/icons/svg/search.svg ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-search\",\n \"use\": \"icon-search-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/search.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/settings .svg": +/*!*************************************!*\ + !*** ./src/icons/svg/settings .svg ***! + \*************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-settings \",\n \"use\": \"icon-settings -usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/settings_.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/shopping.svg": +/*!************************************!*\ + !*** ./src/icons/svg/shopping.svg ***! + \************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-shopping\",\n \"use\": \"icon-shopping-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/shopping.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/size.svg": +/*!********************************!*\ + !*** ./src/icons/svg/size.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-size\",\n \"use\": \"icon-size-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/size.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/skill.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/skill.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-skill\",\n \"use\": \"icon-skill-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/skill.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/star.svg": +/*!********************************!*\ + !*** ./src/icons/svg/star.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-star\",\n \"use\": \"icon-star-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/star.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/statis.svg": +/*!**********************************!*\ + !*** ./src/icons/svg/statis.svg ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-statis\",\n \"use\": \"icon-statis-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/statis.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/stats-dots.svg": +/*!**************************************!*\ + !*** ./src/icons/svg/stats-dots.svg ***! + \**************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-stats-dots\",\n \"use\": \"icon-stats-dots-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/stats-dots.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/stats2.svg": +/*!**********************************!*\ + !*** ./src/icons/svg/stats2.svg ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-stats2\",\n \"use\": \"icon-stats2-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/stats2.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/study.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/study.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-study\",\n \"use\": \"icon-study-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\\n \\n \\n\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/study.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/study1.svg": +/*!**********************************!*\ + !*** ./src/icons/svg/study1.svg ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-study1\",\n \"use\": \"icon-study1-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\\n \\n \\n\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/study1.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/support.svg": +/*!***********************************!*\ + !*** ./src/icons/svg/support.svg ***! + \***********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-support\",\n \"use\": \"icon-support-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/support.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/tab.svg": +/*!*******************************!*\ + !*** ./src/icons/svg/tab.svg ***! + \*******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-tab\",\n \"use\": \"icon-tab-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/tab.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/table.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/table.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-table\",\n \"use\": \"icon-table-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/table.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/test.svg": +/*!********************************!*\ + !*** ./src/icons/svg/test.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-test\",\n \"use\": \"icon-test-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/test.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/theme.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/theme.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-theme\",\n \"use\": \"icon-theme-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/theme.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/topic.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/topic.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-topic\",\n \"use\": \"icon-topic-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/topic.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/training.svg": +/*!************************************!*\ + !*** ./src/icons/svg/training.svg ***! + \************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-training\",\n \"use\": \"icon-training-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\\n \\n \\n\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/training.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/tree-table.svg": +/*!**************************************!*\ + !*** ./src/icons/svg/tree-table.svg ***! + \**************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-tree-table\",\n \"use\": \"icon-tree-table-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/tree-table.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/tree.svg": +/*!********************************!*\ + !*** ./src/icons/svg/tree.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-tree\",\n \"use\": \"icon-tree-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/tree.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/user.svg": +/*!********************************!*\ + !*** ./src/icons/svg/user.svg ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-user\",\n \"use\": \"icon-user-usage\",\n \"viewBox\": \"0 0 130 130\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/user.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/water.svg": +/*!*********************************!*\ + !*** ./src/icons/svg/water.svg ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-water\",\n \"use\": \"icon-water-usage\",\n \"viewBox\": \"0 0 1024 1024\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/water.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/wechat.svg": +/*!**********************************!*\ + !*** ./src/icons/svg/wechat.svg ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-wechat\",\n \"use\": \"icon-wechat-usage\",\n \"viewBox\": \"0 0 128 110\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/wechat.svg?"); + +/***/ }), + +/***/ "./src/icons/svg/zip.svg": +/*!*******************************!*\ + !*** ./src/icons/svg/zip.svg ***! + \*******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! svg-baker-runtime/browser-symbol */ \"./node_modules/svg-baker-runtime/browser-symbol.js\");\n/* harmony import */ var svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! svg-sprite-loader/runtime/browser-sprite.build */ \"./node_modules/svg-sprite-loader/runtime/browser-sprite.build.js\");\n/* harmony import */ var svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1__);\n\n\nvar symbol = new svg_baker_runtime_browser_symbol__WEBPACK_IMPORTED_MODULE_0___default.a({\n \"id\": \"icon-zip\",\n \"use\": \"icon-zip-usage\",\n \"viewBox\": \"0 0 128 128\",\n \"content\": \"\"\n});\nvar result = svg_sprite_loader_runtime_browser_sprite_build__WEBPACK_IMPORTED_MODULE_1___default.a.add(symbol);\n/* harmony default export */ __webpack_exports__[\"default\"] = (symbol);\n\n//# sourceURL=webpack:///./src/icons/svg/zip.svg?"); + +/***/ }), + +/***/ "./src/layout/components/AppMain.vue": +/*!*******************************************!*\ + !*** ./src/layout/components/AppMain.vue ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _AppMain_vue_vue_type_template_id_078753dd_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AppMain.vue?vue&type=template&id=078753dd&scoped=true& */ \"./src/layout/components/AppMain.vue?vue&type=template&id=078753dd&scoped=true&\");\n/* harmony import */ var _AppMain_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./AppMain.vue?vue&type=script&lang=js& */ \"./src/layout/components/AppMain.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _AppMain_vue_vue_type_style_index_0_id_078753dd_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./AppMain.vue?vue&type=style&index=0&id=078753dd&lang=scss&scoped=true& */ \"./src/layout/components/AppMain.vue?vue&type=style&index=0&id=078753dd&lang=scss&scoped=true&\");\n/* harmony import */ var _AppMain_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./AppMain.vue?vue&type=style&index=1&lang=scss& */ \"./src/layout/components/AppMain.vue?vue&type=style&index=1&lang=scss&\");\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(\n _AppMain_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _AppMain_vue_vue_type_template_id_078753dd_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _AppMain_vue_vue_type_template_id_078753dd_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n \"078753dd\",\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/layout/components/AppMain.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/layout/components/AppMain.vue?"); + +/***/ }), + +/***/ "./src/layout/components/AppMain.vue?vue&type=script&lang=js&": +/*!********************************************************************!*\ + !*** ./src/layout/components/AppMain.vue?vue&type=script&lang=js& ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../node_modules/babel-loader/lib!../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../node_modules/vue-loader/lib??vue-loader-options!./AppMain.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/AppMain.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/layout/components/AppMain.vue?"); + +/***/ }), + +/***/ "./src/layout/components/AppMain.vue?vue&type=style&index=0&id=078753dd&lang=scss&scoped=true&": +/*!*****************************************************************************************************!*\ + !*** ./src/layout/components/AppMain.vue?vue&type=style&index=0&id=078753dd&lang=scss&scoped=true& ***! + \*****************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_style_index_0_id_078753dd_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/vue-style-loader??ref--9-oneOf-1-0!../../../node_modules/css-loader/dist/cjs.js??ref--9-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src??ref--9-oneOf-1-2!../../../node_modules/sass-loader/dist/cjs.js??ref--9-oneOf-1-3!../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../node_modules/vue-loader/lib??vue-loader-options!./AppMain.vue?vue&type=style&index=0&id=078753dd&lang=scss&scoped=true& */ \"./node_modules/vue-style-loader/index.js?!./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/dist/cjs.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/AppMain.vue?vue&type=style&index=0&id=078753dd&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_style_index_0_id_078753dd_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_style_index_0_id_078753dd_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_style_index_0_id_078753dd_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__) if([\"default\"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_style_index_0_id_078753dd_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n\n\n//# sourceURL=webpack:///./src/layout/components/AppMain.vue?"); + +/***/ }), + +/***/ "./src/layout/components/AppMain.vue?vue&type=style&index=1&lang=scss&": +/*!*****************************************************************************!*\ + !*** ./src/layout/components/AppMain.vue?vue&type=style&index=1&lang=scss& ***! + \*****************************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/vue-style-loader??ref--9-oneOf-1-0!../../../node_modules/css-loader/dist/cjs.js??ref--9-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src??ref--9-oneOf-1-2!../../../node_modules/sass-loader/dist/cjs.js??ref--9-oneOf-1-3!../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../node_modules/vue-loader/lib??vue-loader-options!./AppMain.vue?vue&type=style&index=1&lang=scss& */ \"./node_modules/vue-style-loader/index.js?!./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/dist/cjs.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/AppMain.vue?vue&type=style&index=1&lang=scss&\");\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_0__) if([\"default\"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n\n\n//# sourceURL=webpack:///./src/layout/components/AppMain.vue?"); + +/***/ }), + +/***/ "./src/layout/components/AppMain.vue?vue&type=template&id=078753dd&scoped=true&": +/*!**************************************************************************************!*\ + !*** ./src/layout/components/AppMain.vue?vue&type=template&id=078753dd&scoped=true& ***! + \**************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_template_id_078753dd_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../node_modules/vue-loader/lib??vue-loader-options!./AppMain.vue?vue&type=template&id=078753dd&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/AppMain.vue?vue&type=template&id=078753dd&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_template_id_078753dd_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_AppMain_vue_vue_type_template_id_078753dd_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/layout/components/AppMain.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Navbar.vue": +/*!******************************************!*\ + !*** ./src/layout/components/Navbar.vue ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Navbar_vue_vue_type_template_id_d16d6306_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Navbar.vue?vue&type=template&id=d16d6306&scoped=true& */ \"./src/layout/components/Navbar.vue?vue&type=template&id=d16d6306&scoped=true&\");\n/* harmony import */ var _Navbar_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Navbar.vue?vue&type=script&lang=js& */ \"./src/layout/components/Navbar.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _Navbar_vue_vue_type_style_index_0_id_d16d6306_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Navbar.vue?vue&type=style&index=0&id=d16d6306&lang=scss&scoped=true& */ \"./src/layout/components/Navbar.vue?vue&type=style&index=0&id=d16d6306&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(\n _Navbar_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _Navbar_vue_vue_type_template_id_d16d6306_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _Navbar_vue_vue_type_template_id_d16d6306_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n \"d16d6306\",\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/layout/components/Navbar.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/layout/components/Navbar.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Navbar.vue?vue&type=script&lang=js&": +/*!*******************************************************************!*\ + !*** ./src/layout/components/Navbar.vue?vue&type=script&lang=js& ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Navbar_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../node_modules/babel-loader/lib!../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../node_modules/vue-loader/lib??vue-loader-options!./Navbar.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Navbar.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Navbar_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/layout/components/Navbar.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Navbar.vue?vue&type=style&index=0&id=d16d6306&lang=scss&scoped=true&": +/*!****************************************************************************************************!*\ + !*** ./src/layout/components/Navbar.vue?vue&type=style&index=0&id=d16d6306&lang=scss&scoped=true& ***! + \****************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Navbar_vue_vue_type_style_index_0_id_d16d6306_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/vue-style-loader??ref--9-oneOf-1-0!../../../node_modules/css-loader/dist/cjs.js??ref--9-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src??ref--9-oneOf-1-2!../../../node_modules/sass-loader/dist/cjs.js??ref--9-oneOf-1-3!../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../node_modules/vue-loader/lib??vue-loader-options!./Navbar.vue?vue&type=style&index=0&id=d16d6306&lang=scss&scoped=true& */ \"./node_modules/vue-style-loader/index.js?!./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/dist/cjs.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Navbar.vue?vue&type=style&index=0&id=d16d6306&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Navbar_vue_vue_type_style_index_0_id_d16d6306_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Navbar_vue_vue_type_style_index_0_id_d16d6306_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Navbar_vue_vue_type_style_index_0_id_d16d6306_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__) if([\"default\"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Navbar_vue_vue_type_style_index_0_id_d16d6306_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n\n\n//# sourceURL=webpack:///./src/layout/components/Navbar.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Navbar.vue?vue&type=template&id=d16d6306&scoped=true&": +/*!*************************************************************************************!*\ + !*** ./src/layout/components/Navbar.vue?vue&type=template&id=d16d6306&scoped=true& ***! + \*************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Navbar_vue_vue_type_template_id_d16d6306_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../node_modules/vue-loader/lib??vue-loader-options!./Navbar.vue?vue&type=template&id=d16d6306&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Navbar.vue?vue&type=template&id=d16d6306&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Navbar_vue_vue_type_template_id_d16d6306_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Navbar_vue_vue_type_template_id_d16d6306_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/layout/components/Navbar.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Settings/index.vue": +/*!**************************************************!*\ + !*** ./src/layout/components/Settings/index.vue ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index_vue_vue_type_template_id_126b135a_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index.vue?vue&type=template&id=126b135a&scoped=true& */ \"./src/layout/components/Settings/index.vue?vue&type=template&id=126b135a&scoped=true&\");\n/* harmony import */ var _index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index.vue?vue&type=script&lang=js& */ \"./src/layout/components/Settings/index.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _index_vue_vue_type_style_index_0_id_126b135a_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index.vue?vue&type=style&index=0&id=126b135a&lang=scss&scoped=true& */ \"./src/layout/components/Settings/index.vue?vue&type=style&index=0&id=126b135a&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(\n _index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _index_vue_vue_type_template_id_126b135a_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _index_vue_vue_type_template_id_126b135a_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n \"126b135a\",\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/layout/components/Settings/index.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/layout/components/Settings/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Settings/index.vue?vue&type=script&lang=js&": +/*!***************************************************************************!*\ + !*** ./src/layout/components/Settings/index.vue?vue&type=script&lang=js& ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../../node_modules/babel-loader/lib!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Settings/index.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/layout/components/Settings/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Settings/index.vue?vue&type=style&index=0&id=126b135a&lang=scss&scoped=true&": +/*!************************************************************************************************************!*\ + !*** ./src/layout/components/Settings/index.vue?vue&type=style&index=0&id=126b135a&lang=scss&scoped=true& ***! + \************************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_126b135a_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/vue-style-loader??ref--9-oneOf-1-0!../../../../node_modules/css-loader/dist/cjs.js??ref--9-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/src??ref--9-oneOf-1-2!../../../../node_modules/sass-loader/dist/cjs.js??ref--9-oneOf-1-3!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=style&index=0&id=126b135a&lang=scss&scoped=true& */ \"./node_modules/vue-style-loader/index.js?!./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/dist/cjs.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Settings/index.vue?vue&type=style&index=0&id=126b135a&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_126b135a_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_126b135a_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_126b135a_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__) if([\"default\"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_126b135a_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n\n\n//# sourceURL=webpack:///./src/layout/components/Settings/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Settings/index.vue?vue&type=template&id=126b135a&scoped=true&": +/*!*********************************************************************************************!*\ + !*** ./src/layout/components/Settings/index.vue?vue&type=template&id=126b135a&scoped=true& ***! + \*********************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_126b135a_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=template&id=126b135a&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Settings/index.vue?vue&type=template&id=126b135a&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_126b135a_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_126b135a_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/layout/components/Settings/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/FixiOSBug.js": +/*!****************************************************!*\ + !*** ./src/layout/components/Sidebar/FixiOSBug.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n computed: {\n device: function device() {\n return this.$store.state.app.device;\n }\n },\n mounted: function mounted() {\n // In order to fix the click on menu on the ios device will trigger the mouseleave bug\n // https://github.com/PanJiaChen/vue-element-admin/issues/1135\n this.fixBugIniOS();\n },\n methods: {\n fixBugIniOS: function fixBugIniOS() {\n var _this = this;\n\n var $subMenu = this.$refs.subMenu;\n\n if ($subMenu) {\n var handleMouseleave = $subMenu.handleMouseleave;\n\n $subMenu.handleMouseleave = function (e) {\n if (_this.device === 'mobile') {\n return;\n }\n\n handleMouseleave(e);\n };\n }\n }\n }\n});\n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/FixiOSBug.js?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/Item.vue": +/*!************************************************!*\ + !*** ./src/layout/components/Sidebar/Item.vue ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Item_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Item.vue?vue&type=script&lang=js& */ \"./src/layout/components/Sidebar/Item.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\nvar render, staticRenderFns\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(\n _Item_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/layout/components/Sidebar/Item.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/Item.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/Item.vue?vue&type=script&lang=js&": +/*!*************************************************************************!*\ + !*** ./src/layout/components/Sidebar/Item.vue?vue&type=script&lang=js& ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Item_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../../node_modules/babel-loader/lib!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./Item.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Sidebar/Item.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Item_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/Item.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/Link.vue": +/*!************************************************!*\ + !*** ./src/layout/components/Sidebar/Link.vue ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Link_vue_vue_type_template_id_32e8ab1a___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Link.vue?vue&type=template&id=32e8ab1a& */ \"./src/layout/components/Sidebar/Link.vue?vue&type=template&id=32e8ab1a&\");\n/* harmony import */ var _Link_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Link.vue?vue&type=script&lang=js& */ \"./src/layout/components/Sidebar/Link.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(\n _Link_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _Link_vue_vue_type_template_id_32e8ab1a___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _Link_vue_vue_type_template_id_32e8ab1a___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/layout/components/Sidebar/Link.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/Link.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/Link.vue?vue&type=script&lang=js&": +/*!*************************************************************************!*\ + !*** ./src/layout/components/Sidebar/Link.vue?vue&type=script&lang=js& ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Link_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../../node_modules/babel-loader/lib!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./Link.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Sidebar/Link.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Link_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/Link.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/Link.vue?vue&type=template&id=32e8ab1a&": +/*!*******************************************************************************!*\ + !*** ./src/layout/components/Sidebar/Link.vue?vue&type=template&id=32e8ab1a& ***! + \*******************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Link_vue_vue_type_template_id_32e8ab1a___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./Link.vue?vue&type=template&id=32e8ab1a& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Sidebar/Link.vue?vue&type=template&id=32e8ab1a&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Link_vue_vue_type_template_id_32e8ab1a___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Link_vue_vue_type_template_id_32e8ab1a___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/Link.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/Logo.vue": +/*!************************************************!*\ + !*** ./src/layout/components/Sidebar/Logo.vue ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Logo_vue_vue_type_template_id_6494804b_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Logo.vue?vue&type=template&id=6494804b&scoped=true& */ \"./src/layout/components/Sidebar/Logo.vue?vue&type=template&id=6494804b&scoped=true&\");\n/* harmony import */ var _Logo_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Logo.vue?vue&type=script&lang=js& */ \"./src/layout/components/Sidebar/Logo.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _Logo_vue_vue_type_style_index_0_id_6494804b_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Logo.vue?vue&type=style&index=0&id=6494804b&lang=scss&scoped=true& */ \"./src/layout/components/Sidebar/Logo.vue?vue&type=style&index=0&id=6494804b&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(\n _Logo_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _Logo_vue_vue_type_template_id_6494804b_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _Logo_vue_vue_type_template_id_6494804b_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n \"6494804b\",\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/layout/components/Sidebar/Logo.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/Logo.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/Logo.vue?vue&type=script&lang=js&": +/*!*************************************************************************!*\ + !*** ./src/layout/components/Sidebar/Logo.vue?vue&type=script&lang=js& ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Logo_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../../node_modules/babel-loader/lib!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./Logo.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Sidebar/Logo.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Logo_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/Logo.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/Logo.vue?vue&type=style&index=0&id=6494804b&lang=scss&scoped=true&": +/*!**********************************************************************************************************!*\ + !*** ./src/layout/components/Sidebar/Logo.vue?vue&type=style&index=0&id=6494804b&lang=scss&scoped=true& ***! + \**********************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Logo_vue_vue_type_style_index_0_id_6494804b_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/vue-style-loader??ref--9-oneOf-1-0!../../../../node_modules/css-loader/dist/cjs.js??ref--9-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/src??ref--9-oneOf-1-2!../../../../node_modules/sass-loader/dist/cjs.js??ref--9-oneOf-1-3!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./Logo.vue?vue&type=style&index=0&id=6494804b&lang=scss&scoped=true& */ \"./node_modules/vue-style-loader/index.js?!./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/dist/cjs.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Sidebar/Logo.vue?vue&type=style&index=0&id=6494804b&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Logo_vue_vue_type_style_index_0_id_6494804b_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Logo_vue_vue_type_style_index_0_id_6494804b_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Logo_vue_vue_type_style_index_0_id_6494804b_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__) if([\"default\"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Logo_vue_vue_type_style_index_0_id_6494804b_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n\n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/Logo.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/Logo.vue?vue&type=template&id=6494804b&scoped=true&": +/*!*******************************************************************************************!*\ + !*** ./src/layout/components/Sidebar/Logo.vue?vue&type=template&id=6494804b&scoped=true& ***! + \*******************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Logo_vue_vue_type_template_id_6494804b_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./Logo.vue?vue&type=template&id=6494804b&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Sidebar/Logo.vue?vue&type=template&id=6494804b&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Logo_vue_vue_type_template_id_6494804b_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Logo_vue_vue_type_template_id_6494804b_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/Logo.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/SidebarItem.vue": +/*!*******************************************************!*\ + !*** ./src/layout/components/Sidebar/SidebarItem.vue ***! + \*******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _SidebarItem_vue_vue_type_template_id_2d2bbdc2___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./SidebarItem.vue?vue&type=template&id=2d2bbdc2& */ \"./src/layout/components/Sidebar/SidebarItem.vue?vue&type=template&id=2d2bbdc2&\");\n/* harmony import */ var _SidebarItem_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./SidebarItem.vue?vue&type=script&lang=js& */ \"./src/layout/components/Sidebar/SidebarItem.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(\n _SidebarItem_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _SidebarItem_vue_vue_type_template_id_2d2bbdc2___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _SidebarItem_vue_vue_type_template_id_2d2bbdc2___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/layout/components/Sidebar/SidebarItem.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/SidebarItem.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/SidebarItem.vue?vue&type=script&lang=js&": +/*!********************************************************************************!*\ + !*** ./src/layout/components/Sidebar/SidebarItem.vue?vue&type=script&lang=js& ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_SidebarItem_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../../node_modules/babel-loader/lib!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./SidebarItem.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Sidebar/SidebarItem.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_SidebarItem_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/SidebarItem.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/SidebarItem.vue?vue&type=template&id=2d2bbdc2&": +/*!**************************************************************************************!*\ + !*** ./src/layout/components/Sidebar/SidebarItem.vue?vue&type=template&id=2d2bbdc2& ***! + \**************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_SidebarItem_vue_vue_type_template_id_2d2bbdc2___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./SidebarItem.vue?vue&type=template&id=2d2bbdc2& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Sidebar/SidebarItem.vue?vue&type=template&id=2d2bbdc2&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_SidebarItem_vue_vue_type_template_id_2d2bbdc2___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_SidebarItem_vue_vue_type_template_id_2d2bbdc2___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/SidebarItem.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/index.vue": +/*!*************************************************!*\ + !*** ./src/layout/components/Sidebar/index.vue ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index_vue_vue_type_template_id_33ec43fc___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index.vue?vue&type=template&id=33ec43fc& */ \"./src/layout/components/Sidebar/index.vue?vue&type=template&id=33ec43fc&\");\n/* harmony import */ var _index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index.vue?vue&type=script&lang=js& */ \"./src/layout/components/Sidebar/index.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(\n _index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _index_vue_vue_type_template_id_33ec43fc___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _index_vue_vue_type_template_id_33ec43fc___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/layout/components/Sidebar/index.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/index.vue?vue&type=script&lang=js&": +/*!**************************************************************************!*\ + !*** ./src/layout/components/Sidebar/index.vue?vue&type=script&lang=js& ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../../node_modules/babel-loader/lib!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Sidebar/index.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/Sidebar/index.vue?vue&type=template&id=33ec43fc&": +/*!********************************************************************************!*\ + !*** ./src/layout/components/Sidebar/index.vue?vue&type=template&id=33ec43fc& ***! + \********************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_33ec43fc___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=template&id=33ec43fc& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/Sidebar/index.vue?vue&type=template&id=33ec43fc&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_33ec43fc___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_33ec43fc___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/layout/components/Sidebar/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/TagsView/ScrollPane.vue": +/*!*******************************************************!*\ + !*** ./src/layout/components/TagsView/ScrollPane.vue ***! + \*******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ScrollPane_vue_vue_type_template_id_be6b7bae_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ScrollPane.vue?vue&type=template&id=be6b7bae&scoped=true& */ \"./src/layout/components/TagsView/ScrollPane.vue?vue&type=template&id=be6b7bae&scoped=true&\");\n/* harmony import */ var _ScrollPane_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ScrollPane.vue?vue&type=script&lang=js& */ \"./src/layout/components/TagsView/ScrollPane.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _ScrollPane_vue_vue_type_style_index_0_id_be6b7bae_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./ScrollPane.vue?vue&type=style&index=0&id=be6b7bae&lang=scss&scoped=true& */ \"./src/layout/components/TagsView/ScrollPane.vue?vue&type=style&index=0&id=be6b7bae&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(\n _ScrollPane_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _ScrollPane_vue_vue_type_template_id_be6b7bae_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _ScrollPane_vue_vue_type_template_id_be6b7bae_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n \"be6b7bae\",\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/layout/components/TagsView/ScrollPane.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/layout/components/TagsView/ScrollPane.vue?"); + +/***/ }), + +/***/ "./src/layout/components/TagsView/ScrollPane.vue?vue&type=script&lang=js&": +/*!********************************************************************************!*\ + !*** ./src/layout/components/TagsView/ScrollPane.vue?vue&type=script&lang=js& ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ScrollPane_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../../node_modules/babel-loader/lib!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./ScrollPane.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/TagsView/ScrollPane.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ScrollPane_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/layout/components/TagsView/ScrollPane.vue?"); + +/***/ }), + +/***/ "./src/layout/components/TagsView/ScrollPane.vue?vue&type=style&index=0&id=be6b7bae&lang=scss&scoped=true&": +/*!*****************************************************************************************************************!*\ + !*** ./src/layout/components/TagsView/ScrollPane.vue?vue&type=style&index=0&id=be6b7bae&lang=scss&scoped=true& ***! + \*****************************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ScrollPane_vue_vue_type_style_index_0_id_be6b7bae_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/vue-style-loader??ref--9-oneOf-1-0!../../../../node_modules/css-loader/dist/cjs.js??ref--9-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/src??ref--9-oneOf-1-2!../../../../node_modules/sass-loader/dist/cjs.js??ref--9-oneOf-1-3!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./ScrollPane.vue?vue&type=style&index=0&id=be6b7bae&lang=scss&scoped=true& */ \"./node_modules/vue-style-loader/index.js?!./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/dist/cjs.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/TagsView/ScrollPane.vue?vue&type=style&index=0&id=be6b7bae&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ScrollPane_vue_vue_type_style_index_0_id_be6b7bae_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ScrollPane_vue_vue_type_style_index_0_id_be6b7bae_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ScrollPane_vue_vue_type_style_index_0_id_be6b7bae_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__) if([\"default\"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ScrollPane_vue_vue_type_style_index_0_id_be6b7bae_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n\n\n//# sourceURL=webpack:///./src/layout/components/TagsView/ScrollPane.vue?"); + +/***/ }), + +/***/ "./src/layout/components/TagsView/ScrollPane.vue?vue&type=template&id=be6b7bae&scoped=true&": +/*!**************************************************************************************************!*\ + !*** ./src/layout/components/TagsView/ScrollPane.vue?vue&type=template&id=be6b7bae&scoped=true& ***! + \**************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ScrollPane_vue_vue_type_template_id_be6b7bae_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./ScrollPane.vue?vue&type=template&id=be6b7bae&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/TagsView/ScrollPane.vue?vue&type=template&id=be6b7bae&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ScrollPane_vue_vue_type_template_id_be6b7bae_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ScrollPane_vue_vue_type_template_id_be6b7bae_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/layout/components/TagsView/ScrollPane.vue?"); + +/***/ }), + +/***/ "./src/layout/components/TagsView/index.vue": +/*!**************************************************!*\ + !*** ./src/layout/components/TagsView/index.vue ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index_vue_vue_type_template_id_fac8ca64_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index.vue?vue&type=template&id=fac8ca64&scoped=true& */ \"./src/layout/components/TagsView/index.vue?vue&type=template&id=fac8ca64&scoped=true&\");\n/* harmony import */ var _index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index.vue?vue&type=script&lang=js& */ \"./src/layout/components/TagsView/index.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _index_vue_vue_type_style_index_0_id_fac8ca64_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index.vue?vue&type=style&index=0&id=fac8ca64&lang=scss&scoped=true& */ \"./src/layout/components/TagsView/index.vue?vue&type=style&index=0&id=fac8ca64&lang=scss&scoped=true&\");\n/* harmony import */ var _index_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./index.vue?vue&type=style&index=1&lang=scss& */ \"./src/layout/components/TagsView/index.vue?vue&type=style&index=1&lang=scss&\");\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(\n _index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _index_vue_vue_type_template_id_fac8ca64_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _index_vue_vue_type_template_id_fac8ca64_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n \"fac8ca64\",\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/layout/components/TagsView/index.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/layout/components/TagsView/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/TagsView/index.vue?vue&type=script&lang=js&": +/*!***************************************************************************!*\ + !*** ./src/layout/components/TagsView/index.vue?vue&type=script&lang=js& ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../../../node_modules/babel-loader/lib!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/TagsView/index.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/layout/components/TagsView/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/TagsView/index.vue?vue&type=style&index=0&id=fac8ca64&lang=scss&scoped=true&": +/*!************************************************************************************************************!*\ + !*** ./src/layout/components/TagsView/index.vue?vue&type=style&index=0&id=fac8ca64&lang=scss&scoped=true& ***! + \************************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_fac8ca64_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/vue-style-loader??ref--9-oneOf-1-0!../../../../node_modules/css-loader/dist/cjs.js??ref--9-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/src??ref--9-oneOf-1-2!../../../../node_modules/sass-loader/dist/cjs.js??ref--9-oneOf-1-3!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=style&index=0&id=fac8ca64&lang=scss&scoped=true& */ \"./node_modules/vue-style-loader/index.js?!./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/dist/cjs.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/TagsView/index.vue?vue&type=style&index=0&id=fac8ca64&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_fac8ca64_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_fac8ca64_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_fac8ca64_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__) if([\"default\"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_fac8ca64_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n\n\n//# sourceURL=webpack:///./src/layout/components/TagsView/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/TagsView/index.vue?vue&type=style&index=1&lang=scss&": +/*!************************************************************************************!*\ + !*** ./src/layout/components/TagsView/index.vue?vue&type=style&index=1&lang=scss& ***! + \************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/vue-style-loader??ref--9-oneOf-1-0!../../../../node_modules/css-loader/dist/cjs.js??ref--9-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/src??ref--9-oneOf-1-2!../../../../node_modules/sass-loader/dist/cjs.js??ref--9-oneOf-1-3!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=style&index=1&lang=scss& */ \"./node_modules/vue-style-loader/index.js?!./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/dist/cjs.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/TagsView/index.vue?vue&type=style&index=1&lang=scss&\");\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_0__) if([\"default\"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_1_lang_scss___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n\n\n//# sourceURL=webpack:///./src/layout/components/TagsView/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/TagsView/index.vue?vue&type=template&id=fac8ca64&scoped=true&": +/*!*********************************************************************************************!*\ + !*** ./src/layout/components/TagsView/index.vue?vue&type=template&id=fac8ca64&scoped=true& ***! + \*********************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_fac8ca64_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=template&id=fac8ca64&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/components/TagsView/index.vue?vue&type=template&id=fac8ca64&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_fac8ca64_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_fac8ca64_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/layout/components/TagsView/index.vue?"); + +/***/ }), + +/***/ "./src/layout/components/index.js": +/*!****************************************!*\ + !*** ./src/layout/components/index.js ***! + \****************************************/ +/*! exports provided: AppMain, Navbar, Settings, Sidebar, TagsView */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _AppMain__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AppMain */ \"./src/layout/components/AppMain.vue\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"AppMain\", function() { return _AppMain__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _Navbar__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Navbar */ \"./src/layout/components/Navbar.vue\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"Navbar\", function() { return _Navbar__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _Settings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Settings */ \"./src/layout/components/Settings/index.vue\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"Settings\", function() { return _Settings__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _Sidebar_index_vue__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Sidebar/index.vue */ \"./src/layout/components/Sidebar/index.vue\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"Sidebar\", function() { return _Sidebar_index_vue__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _TagsView_index_vue__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./TagsView/index.vue */ \"./src/layout/components/TagsView/index.vue\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"TagsView\", function() { return _TagsView_index_vue__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./src/layout/components/index.js?"); + +/***/ }), + +/***/ "./src/layout/index.vue": +/*!******************************!*\ + !*** ./src/layout/index.vue ***! + \******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index_vue_vue_type_template_id_13877386_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index.vue?vue&type=template&id=13877386&scoped=true& */ \"./src/layout/index.vue?vue&type=template&id=13877386&scoped=true&\");\n/* harmony import */ var _index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index.vue?vue&type=script&lang=js& */ \"./src/layout/index.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport *//* harmony import */ var _index_vue_vue_type_style_index_0_id_13877386_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index.vue?vue&type=style&index=0&id=13877386&lang=scss&scoped=true& */ \"./src/layout/index.vue?vue&type=style&index=0&id=13877386&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(\n _index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _index_vue_vue_type_template_id_13877386_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _index_vue_vue_type_template_id_13877386_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n \"13877386\",\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/layout/index.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./src/layout/index.vue?"); + +/***/ }), + +/***/ "./src/layout/index.vue?vue&type=script&lang=js&": +/*!*******************************************************!*\ + !*** ./src/layout/index.vue?vue&type=script&lang=js& ***! + \*******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../node_modules/cache-loader/dist/cjs.js??ref--13-0!../../node_modules/babel-loader/lib!../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=script&lang=js& */ \"./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/index.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_cache_loader_dist_cjs_js_ref_13_0_node_modules_babel_loader_lib_index_js_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./src/layout/index.vue?"); + +/***/ }), + +/***/ "./src/layout/index.vue?vue&type=style&index=0&id=13877386&lang=scss&scoped=true&": +/*!****************************************************************************************!*\ + !*** ./src/layout/index.vue?vue&type=style&index=0&id=13877386&lang=scss&scoped=true& ***! + \****************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_13877386_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../node_modules/vue-style-loader??ref--9-oneOf-1-0!../../node_modules/css-loader/dist/cjs.js??ref--9-oneOf-1-1!../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../node_modules/postcss-loader/src??ref--9-oneOf-1-2!../../node_modules/sass-loader/dist/cjs.js??ref--9-oneOf-1-3!../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=style&index=0&id=13877386&lang=scss&scoped=true& */ \"./node_modules/vue-style-loader/index.js?!./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/dist/cjs.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/index.vue?vue&type=style&index=0&id=13877386&lang=scss&scoped=true&\");\n/* harmony import */ var _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_13877386_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_13877386_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_13877386_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__) if([\"default\"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_vue_style_loader_index_js_ref_9_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_9_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_9_oneOf_1_2_node_modules_sass_loader_dist_cjs_js_ref_9_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_id_13877386_lang_scss_scoped_true___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n\n\n//# sourceURL=webpack:///./src/layout/index.vue?"); + +/***/ }), + +/***/ "./src/layout/index.vue?vue&type=template&id=13877386&scoped=true&": +/*!*************************************************************************!*\ + !*** ./src/layout/index.vue?vue&type=template&id=13877386&scoped=true& ***! + \*************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_13877386_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"9323b05c-vue-loader-template\"}!../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../node_modules/cache-loader/dist/cjs.js??ref--1-0!../../node_modules/vue-loader/lib??vue-loader-options!./index.vue?vue&type=template&id=13877386&scoped=true& */ \"./node_modules/cache-loader/dist/cjs.js?{\\\"cacheDirectory\\\":\\\"node_modules/.cache/vue-loader\\\",\\\"cacheIdentifier\\\":\\\"9323b05c-vue-loader-template\\\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/layout/index.vue?vue&type=template&id=13877386&scoped=true&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_13877386_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_cache_loader_dist_cjs_js_cacheDirectory_node_modules_cache_vue_loader_cacheIdentifier_9323b05c_vue_loader_template_node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_cache_loader_dist_cjs_js_ref_1_0_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_template_id_13877386_scoped_true___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./src/layout/index.vue?"); + +/***/ }), + +/***/ "./src/layout/mixin/ResizeHandler.js": +/*!*******************************************!*\ + !*** ./src/layout/mixin/ResizeHandler.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @/store */ \"./src/store/index.js\");\n\nvar _document = document,\n body = _document.body;\nvar WIDTH = 992; // refer to Bootstrap's responsive design\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n watch: {\n $route: function $route(route) {\n if (this.device === 'mobile' && this.sidebar.opened) {\n _store__WEBPACK_IMPORTED_MODULE_0__[\"default\"].dispatch('app/closeSideBar', {\n withoutAnimation: false\n });\n }\n }\n },\n beforeMount: function beforeMount() {\n window.addEventListener('resize', this.$_resizeHandler);\n },\n beforeDestroy: function beforeDestroy() {\n window.removeEventListener('resize', this.$_resizeHandler);\n },\n mounted: function mounted() {\n var isMobile = this.$_isMobile();\n\n if (isMobile) {\n _store__WEBPACK_IMPORTED_MODULE_0__[\"default\"].dispatch('app/toggleDevice', 'mobile');\n _store__WEBPACK_IMPORTED_MODULE_0__[\"default\"].dispatch('app/closeSideBar', {\n withoutAnimation: true\n });\n }\n },\n methods: {\n // use $_ for mixins properties\n // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential\n $_isMobile: function $_isMobile() {\n var rect = body.getBoundingClientRect();\n return rect.width - 1 < WIDTH;\n },\n $_resizeHandler: function $_resizeHandler() {\n if (!document.hidden) {\n var isMobile = this.$_isMobile();\n _store__WEBPACK_IMPORTED_MODULE_0__[\"default\"].dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop');\n\n if (isMobile) {\n _store__WEBPACK_IMPORTED_MODULE_0__[\"default\"].dispatch('app/closeSideBar', {\n withoutAnimation: true\n });\n }\n }\n }\n }\n});\n\n//# sourceURL=webpack:///./src/layout/mixin/ResizeHandler.js?"); + +/***/ }), + +/***/ "./src/main.js": +/*!*********************!*\ + !*** ./src/main.js ***! + \*********************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var core_js_modules_es6_object_keys__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core-js/modules/es6.object.keys */ \"./node_modules/core-js/modules/es6.object.keys.js\");\n/* harmony import */ var core_js_modules_es6_object_keys__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es6_object_keys__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! core-js/modules/web.dom.iterable */ \"./node_modules/core-js/modules/web.dom.iterable.js\");\n/* harmony import */ var core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es6_array_iterator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./node_modules/core-js/modules/es6.array.iterator.js */ \"./node_modules/core-js/modules/es6.array.iterator.js\");\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es6_array_iterator_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es6_array_iterator_js__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es6_promise_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./node_modules/core-js/modules/es6.promise.js */ \"./node_modules/core-js/modules/es6.promise.js\");\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es6_promise_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es6_promise_js__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es6_object_assign_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./node_modules/core-js/modules/es6.object.assign.js */ \"./node_modules/core-js/modules/es6.object.assign.js\");\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es6_object_assign_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es6_object_assign_js__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es7_promise_finally_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./node_modules/core-js/modules/es7.promise.finally.js */ \"./node_modules/core-js/modules/es7.promise.finally.js\");\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es7_promise_finally_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_core_js_modules_es7_promise_finally_js__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.runtime.esm.js\");\n/* harmony import */ var js_cookie__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! js-cookie */ \"./node_modules/js-cookie/src/js.cookie.js\");\n/* harmony import */ var js_cookie__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(js_cookie__WEBPACK_IMPORTED_MODULE_7__);\n/* harmony import */ var normalize_css_normalize_css__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! normalize.css/normalize.css */ \"./node_modules/normalize.css/normalize.css\");\n/* harmony import */ var normalize_css_normalize_css__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(normalize_css_normalize_css__WEBPACK_IMPORTED_MODULE_8__);\n/* harmony import */ var element_ui__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! element-ui */ \"./node_modules/element-ui/lib/element-ui.common.js\");\n/* harmony import */ var element_ui__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(element_ui__WEBPACK_IMPORTED_MODULE_9__);\n/* harmony import */ var _styles_element_variables_scss__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./styles/element-variables.scss */ \"./src/styles/element-variables.scss\");\n/* harmony import */ var _styles_element_variables_scss__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(_styles_element_variables_scss__WEBPACK_IMPORTED_MODULE_10__);\n/* harmony import */ var _styles_index_scss__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! @/styles/index.scss */ \"./src/styles/index.scss\");\n/* harmony import */ var _styles_index_scss__WEBPACK_IMPORTED_MODULE_11___default = /*#__PURE__*/__webpack_require__.n(_styles_index_scss__WEBPACK_IMPORTED_MODULE_11__);\n/* harmony import */ var _App__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./App */ \"./src/App.vue\");\n/* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./store */ \"./src/store/index.js\");\n/* harmony import */ var _router__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./router */ \"./src/router/index.js\");\n/* harmony import */ var _icons__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./icons */ \"./src/icons/index.js\");\n/* harmony import */ var _permission__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./permission */ \"./src/permission.js\");\n/* harmony import */ var _utils_error_log__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./utils/error-log */ \"./src/utils/error-log.js\");\n/* harmony import */ var _filters__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./filters */ \"./src/filters/index.js\");\n\n\n\n\n\n\n\n\n // a modern alternative to CSS resets\n\n\n\n\n\n\n\n // icon\n\n // permission control\n\n // error log\n\n // Element UI\n\nvue__WEBPACK_IMPORTED_MODULE_6__[\"default\"].use(element_ui__WEBPACK_IMPORTED_MODULE_9___default.a, {\n size: js_cookie__WEBPACK_IMPORTED_MODULE_7___default.a.get('size') || 'medium' // set element-ui default size\n\n}); // 注册全局过滤器\n\nObject.keys(_filters__WEBPACK_IMPORTED_MODULE_18__).forEach(function (key) {\n vue__WEBPACK_IMPORTED_MODULE_6__[\"default\"].filter(key, _filters__WEBPACK_IMPORTED_MODULE_18__[key]);\n});\nvue__WEBPACK_IMPORTED_MODULE_6__[\"default\"].config.productionTip = false; // 环境标识\n\nvue__WEBPACK_IMPORTED_MODULE_6__[\"default\"].prototype.$demo = \"demo\" === 'demo';\nnew vue__WEBPACK_IMPORTED_MODULE_6__[\"default\"]({\n el: '#app',\n router: _router__WEBPACK_IMPORTED_MODULE_14__[\"default\"],\n store: _store__WEBPACK_IMPORTED_MODULE_13__[\"default\"],\n render: function render(h) {\n return h(_App__WEBPACK_IMPORTED_MODULE_12__[\"default\"]);\n }\n});\n\n//# sourceURL=webpack:///./src/main.js?"); + +/***/ }), + +/***/ "./src/permission.js": +/*!***************************!*\ + !*** ./src/permission.js ***! + \***************************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./node_modules/@babel/runtime-corejs2/helpers/esm/objectSpread2.js */ \"./node_modules/@babel/runtime-corejs2/helpers/esm/objectSpread2.js\");\n/* harmony import */ var regenerator_runtime_runtime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! regenerator-runtime/runtime */ \"./node_modules/regenerator-runtime/runtime.js\");\n/* harmony import */ var regenerator_runtime_runtime__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(regenerator_runtime_runtime__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./node_modules/@babel/runtime-corejs2/helpers/esm/asyncToGenerator.js */ \"./node_modules/@babel/runtime-corejs2/helpers/esm/asyncToGenerator.js\");\n/* harmony import */ var _router__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./router */ \"./src/router/index.js\");\n/* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./store */ \"./src/store/index.js\");\n/* harmony import */ var element_ui__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! element-ui */ \"./node_modules/element-ui/lib/element-ui.common.js\");\n/* harmony import */ var element_ui__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(element_ui__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var nprogress__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! nprogress */ \"./node_modules/nprogress/nprogress.js\");\n/* harmony import */ var nprogress__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(nprogress__WEBPACK_IMPORTED_MODULE_6__);\n/* harmony import */ var nprogress_nprogress_css__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! nprogress/nprogress.css */ \"./node_modules/nprogress/nprogress.css\");\n/* harmony import */ var nprogress_nprogress_css__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(nprogress_nprogress_css__WEBPACK_IMPORTED_MODULE_7__);\n/* harmony import */ var _utils_auth__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @/utils/auth */ \"./src/utils/auth.js\");\n/* harmony import */ var _utils_get_page_title__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @/utils/get-page-title */ \"./src/utils/get-page-title.js\");\n\n\n\n\n\n\n // progress bar\n\n // progress bar style\n\n // get token from cookie\n\n\nnprogress__WEBPACK_IMPORTED_MODULE_6___default.a.configure({\n showSpinner: false\n}); // NProgress Configuration\n\nvar whiteList = ['/login', '/register']; // no redirect whitelist\n\n_router__WEBPACK_IMPORTED_MODULE_3__[\"default\"].beforeEach( /*#__PURE__*/function () {\n var _ref = Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])( /*#__PURE__*/regeneratorRuntime.mark(function _callee(to, from, next) {\n var siteData, hasToken, hasRoles, _yield$store$dispatch, roles, accessRoutes;\n\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n // start progress bar\n nprogress__WEBPACK_IMPORTED_MODULE_6___default.a.start(); // 获取网站基本信息\n\n siteData = _store__WEBPACK_IMPORTED_MODULE_4__[\"default\"].getters.siteData;\n\n if (siteData.siteName) {\n _context.next = 6;\n break;\n }\n\n _context.next = 5;\n return _store__WEBPACK_IMPORTED_MODULE_4__[\"default\"].dispatch('settings/getSite');\n\n case 5:\n siteData = _context.sent;\n\n case 6:\n // 页面标题\n document.title = Object(_utils_get_page_title__WEBPACK_IMPORTED_MODULE_9__[\"default\"])(siteData.siteName, to.meta.title); // 本地token\n\n hasToken = Object(_utils_auth__WEBPACK_IMPORTED_MODULE_8__[\"getToken\"])();\n\n if (!hasToken) {\n _context.next = 40;\n break;\n }\n\n if (!(to.path === '/login')) {\n _context.next = 14;\n break;\n }\n\n next({\n path: '/'\n });\n nprogress__WEBPACK_IMPORTED_MODULE_6___default.a.done();\n _context.next = 38;\n break;\n\n case 14:\n hasRoles = _store__WEBPACK_IMPORTED_MODULE_4__[\"default\"].getters.roles && _store__WEBPACK_IMPORTED_MODULE_4__[\"default\"].getters.roles.length > 0;\n\n if (!hasRoles) {\n _context.next = 19;\n break;\n }\n\n next();\n _context.next = 38;\n break;\n\n case 19:\n _context.prev = 19;\n _context.next = 22;\n return _store__WEBPACK_IMPORTED_MODULE_4__[\"default\"].dispatch('user/getInfo');\n\n case 22:\n _yield$store$dispatch = _context.sent;\n roles = _yield$store$dispatch.roles;\n _context.next = 26;\n return _store__WEBPACK_IMPORTED_MODULE_4__[\"default\"].dispatch('permission/generateRoutes', roles);\n\n case 26:\n accessRoutes = _context.sent;\n _router__WEBPACK_IMPORTED_MODULE_3__[\"default\"].addRoutes(accessRoutes);\n next(Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, to), {}, {\n replace: true\n }));\n _context.next = 38;\n break;\n\n case 31:\n _context.prev = 31;\n _context.t0 = _context[\"catch\"](19);\n _context.next = 35;\n return _store__WEBPACK_IMPORTED_MODULE_4__[\"default\"].dispatch('user/resetToken');\n\n case 35:\n element_ui__WEBPACK_IMPORTED_MODULE_5__[\"Message\"].error(_context.t0 || 'Has Error');\n next(\"/login?redirect=\".concat(to.path));\n nprogress__WEBPACK_IMPORTED_MODULE_6___default.a.done();\n\n case 38:\n _context.next = 41;\n break;\n\n case 40:\n // 排除白名单\n if (whiteList.indexOf(to.path) !== -1) {\n next();\n } else {\n // 跳转到登录页面\n next(\"/login?redirect=\".concat(to.path));\n nprogress__WEBPACK_IMPORTED_MODULE_6___default.a.done();\n }\n\n case 41:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee, null, [[19, 31]]);\n }));\n\n return function (_x, _x2, _x3) {\n return _ref.apply(this, arguments);\n };\n}());\n_router__WEBPACK_IMPORTED_MODULE_3__[\"default\"].afterEach(function () {\n // finish progress bar\n nprogress__WEBPACK_IMPORTED_MODULE_6___default.a.done();\n});\n\n//# sourceURL=webpack:///./src/permission.js?"); + +/***/ }), + +/***/ "./src/router/index.js": +/*!*****************************!*\ + !*** ./src/router/index.js ***! + \*****************************/ +/*! exports provided: constantRoutes, asyncRoutes, resetRouter, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"constantRoutes\", function() { return constantRoutes; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"asyncRoutes\", function() { return asyncRoutes; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"resetRouter\", function() { return resetRouter; });\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.runtime.esm.js\");\n/* harmony import */ var vue_router__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! vue-router */ \"./node_modules/vue-router/dist/vue-router.esm.js\");\n/* harmony import */ var _layout__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @/layout */ \"./src/layout/index.vue\");\n/* harmony import */ var _views_login_components_LoginLayout__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @/views/login/components/LoginLayout */ \"./src/views/login/components/LoginLayout.vue\");\n\n\nvue__WEBPACK_IMPORTED_MODULE_0__[\"default\"].use(vue_router__WEBPACK_IMPORTED_MODULE_1__[\"default\"]); // 主要框架\n\n // 登录框架\n\n\nvar constantRoutes = [{\n path: '/redirect',\n component: _layout__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n hidden: true,\n children: [{\n path: '/redirect/:path*',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 27).then(__webpack_require__.bind(null, /*! @/views/redirect/index */ \"./src/views/redirect/index.vue\"));\n }\n }]\n}, {\n path: '/login',\n component: _views_login_components_LoginLayout__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n hidden: true,\n children: [{\n path: '/login',\n name: 'Login',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 25).then(__webpack_require__.bind(null, /*! @/views/login/index */ \"./src/views/login/index.vue\"));\n },\n hidden: true\n }, {\n path: '/register',\n name: 'Register',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 26).then(__webpack_require__.bind(null, /*! @/views/login/register */ \"./src/views/login/register.vue\"));\n },\n hidden: true\n }]\n}, {\n path: '/404',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 12).then(__webpack_require__.bind(null, /*! @/views/error-page/404 */ \"./src/views/error-page/404.vue\"));\n },\n hidden: true\n}, {\n path: '/401',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 16).then(__webpack_require__.bind(null, /*! @/views/error-page/401 */ \"./src/views/error-page/401.vue\"));\n },\n hidden: true\n}, {\n path: '/',\n component: _layout__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n redirect: '/dashboard',\n children: [{\n path: 'dashboard',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 11).then(__webpack_require__.bind(null, /*! @/views/dashboard/index */ \"./src/views/dashboard/index.vue\"));\n },\n name: 'Dashboard',\n meta: {\n title: '控制台',\n icon: 'dashboard',\n affix: true\n }\n }, {\n path: 'qu/view/:id',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 18).then(__webpack_require__.bind(null, /*! @/views/qu/qu/view */ \"./src/views/qu/qu/view.vue\"));\n },\n name: 'ViewQu',\n meta: {\n title: '题目详情',\n noCache: true,\n activeMenu: '/manage/qu'\n },\n hidden: true\n }]\n}, {\n path: '/profile',\n component: _layout__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n redirect: '/profile/index',\n hidden: true,\n children: [{\n path: 'index',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 4).then(__webpack_require__.bind(null, /*! @/views/profile/index */ \"./src/views/profile/index.vue\"));\n },\n name: 'Profile',\n meta: {\n title: '个人资料',\n icon: 'user',\n noCache: true\n }\n }]\n}];\n/**\n * asyncRoutes\n * the routes that need to be dynamically loaded based on user roles\n */\n\nvar asyncRoutes = [{\n path: '/exam/start/:id',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 5).then(__webpack_require__.bind(null, /*! @/views/paper/exam/exam */ \"./src/views/paper/exam/exam.vue\"));\n },\n name: 'StartExam',\n meta: {\n title: '开始考试'\n },\n hidden: true\n}, {\n path: '/my',\n component: _layout__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n redirect: '/my/exam',\n name: 'Online',\n meta: {\n title: '在线考试',\n icon: 'list',\n roles: ['student', 'sa']\n },\n children: [{\n path: 'exam',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(19)]).then(__webpack_require__.bind(null, /*! @/views/paper/exam/list */ \"./src/views/paper/exam/list.vue\"));\n },\n name: 'ExamOnline',\n meta: {\n title: '在线考试',\n noCache: true,\n icon: 'guide'\n }\n }, {\n path: 'exam/prepare/:examId',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 13).then(__webpack_require__.bind(null, /*! @/views/paper/exam/preview */ \"./src/views/paper/exam/preview.vue\"));\n },\n name: 'PreExam',\n meta: {\n title: '准备考试',\n noCache: true,\n activeMenu: '/my/exam'\n },\n hidden: true\n }, {\n path: 'exam/result/:id',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 17).then(__webpack_require__.bind(null, /*! @/views/paper/exam/result */ \"./src/views/paper/exam/result.vue\"));\n },\n name: 'ShowExam',\n meta: {\n title: '考试结果',\n noCache: true,\n activeMenu: '/online/exam'\n },\n hidden: true\n }, {\n path: 'exam/records',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(7)]).then(__webpack_require__.bind(null, /*! @/views/user/exam/my */ \"./src/views/user/exam/my.vue\"));\n },\n name: 'ListMyExam',\n meta: {\n title: '我的成绩',\n noCache: true,\n icon: 'results'\n }\n }, {\n path: 'book/list/:examId',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(24)]).then(__webpack_require__.bind(null, /*! @/views/user/book */ \"./src/views/user/book/index.vue\"));\n },\n name: 'BookList',\n meta: {\n title: '考试错题',\n noCache: true,\n activeMenu: '/my/exam/records'\n },\n hidden: true\n }, {\n path: 'book/training/:examId',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 15).then(__webpack_require__.bind(null, /*! @/views/user/book/train */ \"./src/views/user/book/train.vue\"));\n },\n name: 'BookTraining',\n meta: {\n title: '错题训练',\n noCache: true,\n activeMenu: '/my/exam/records'\n },\n hidden: true\n }]\n}, {\n path: '/exam',\n component: _layout__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n redirect: '/exam/repo',\n name: 'Manage',\n meta: {\n title: '考试管理',\n icon: 'example',\n roles: ['sa', 'teacher']\n },\n children: [{\n path: 'repo',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(21)]).then(__webpack_require__.bind(null, /*! @/views/qu/repo */ \"./src/views/qu/repo/index.vue\"));\n },\n name: 'ListRepo',\n meta: {\n title: '题库管理',\n noCache: true,\n icon: 'repo'\n }\n }, {\n path: 'repo/add',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 3).then(__webpack_require__.bind(null, /*! @/views/qu/repo/form */ \"./src/views/qu/repo/form.vue\"));\n },\n name: 'AddRepo',\n meta: {\n title: '添加题库',\n noCache: true,\n activeMenu: '/exam/repo'\n },\n hidden: true\n }, {\n path: 'repo/update/:id',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 3).then(__webpack_require__.bind(null, /*! @/views/qu/repo/form */ \"./src/views/qu/repo/form.vue\"));\n },\n name: 'UpdateRepo',\n meta: {\n title: '题库详情',\n noCache: true,\n activeMenu: '/exam/repo'\n },\n hidden: true\n }, {\n path: 'qu',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(14)]).then(__webpack_require__.bind(null, /*! @/views/qu/qu */ \"./src/views/qu/qu/index.vue\"));\n },\n name: 'ListQu',\n meta: {\n title: '试题管理',\n noCache: true,\n icon: 'support'\n }\n }, {\n path: 'qu/add',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(0)]).then(__webpack_require__.bind(null, /*! @/views/qu/qu/form */ \"./src/views/qu/qu/form.vue\"));\n },\n name: 'AddQu',\n meta: {\n title: '添加试题',\n noCache: true,\n activeMenu: '/exam/qu'\n },\n hidden: true\n }, {\n path: 'qu/update/:id',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(0)]).then(__webpack_require__.bind(null, /*! @/views/qu/qu/form */ \"./src/views/qu/qu/form.vue\"));\n },\n name: 'UpdateQu',\n meta: {\n title: '修改试题',\n noCache: true,\n activeMenu: '/exam/qu'\n },\n hidden: true\n }, {\n path: 'exam',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(20)]).then(__webpack_require__.bind(null, /*! @/views/exam/exam */ \"./src/views/exam/exam/index.vue\"));\n },\n name: 'ListExam',\n meta: {\n title: '考试管理',\n noCache: true,\n icon: 'log'\n }\n }, {\n path: 'exam/add',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(2)]).then(__webpack_require__.bind(null, /*! @/views/exam/exam/form */ \"./src/views/exam/exam/form.vue\"));\n },\n name: 'AddExam',\n meta: {\n title: '添加考试',\n noCache: true,\n activeMenu: '/exam/exam'\n },\n hidden: true\n }, {\n path: 'exam/update/:id',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(2)]).then(__webpack_require__.bind(null, /*! @/views/exam/exam/form */ \"./src/views/exam/exam/form.vue\"));\n },\n name: 'UpdateExam',\n meta: {\n title: '修改考试',\n noCache: true,\n activeMenu: '/exam/exam'\n },\n hidden: true\n }, {\n path: 'exam/users/:examId',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(6)]).then(__webpack_require__.bind(null, /*! @/views/user/exam */ \"./src/views/user/exam/index.vue\"));\n },\n name: 'ListExamUser',\n meta: {\n title: '考试人员',\n noCache: true,\n activeMenu: '/exam/exam'\n },\n hidden: true\n }, {\n path: 'exam/paper/:examId',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(1), __webpack_require__.e(9)]).then(__webpack_require__.bind(null, /*! @/views/paper/paper */ \"./src/views/paper/paper/index.vue\"));\n },\n name: 'ListPaper',\n meta: {\n title: '考试记录',\n noCache: true,\n activeMenu: '/exam/exam'\n },\n hidden: true\n }]\n}, {\n path: '/sys',\n component: _layout__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n redirect: '/sys/config',\n name: 'Sys',\n meta: {\n title: '系统设置',\n icon: 'configure',\n roles: ['sa']\n },\n children: [{\n path: 'config',\n component: function component() {\n return __webpack_require__.e(/*! import() */ 8).then(__webpack_require__.bind(null, /*! @/views/sys/config */ \"./src/views/sys/config/index.vue\"));\n },\n name: 'SysConfig',\n meta: {\n title: '系统配置',\n icon: 'theme'\n }\n }, {\n path: 'depart',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(22)]).then(__webpack_require__.bind(null, /*! @/views/sys/depart */ \"./src/views/sys/depart/index.vue\"));\n },\n name: 'SysDepart',\n meta: {\n title: '部门管理',\n icon: 'tree'\n }\n }, {\n path: 'role',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(23)]).then(__webpack_require__.bind(null, /*! @/views/sys/role */ \"./src/views/sys/role/index.vue\"));\n },\n name: 'SysRole',\n meta: {\n title: '角色管理',\n icon: 'role'\n }\n }, {\n path: 'user',\n component: function component() {\n return Promise.all(/*! import() */[__webpack_require__.e(\"chunk-commons\"), __webpack_require__.e(1), __webpack_require__.e(10)]).then(__webpack_require__.bind(null, /*! @/views/sys/user */ \"./src/views/sys/user/index.vue\"));\n },\n name: 'SysUser',\n meta: {\n title: '用户管理',\n icon: 'admin'\n }\n }]\n}, // 404 page must be placed at the end !!!\n{\n path: '*',\n redirect: '/dashboard',\n hidden: true\n}];\n\nvar createRouter = function createRouter() {\n return new vue_router__WEBPACK_IMPORTED_MODULE_1__[\"default\"]({\n // mode: 'history', // require service support\n scrollBehavior: function scrollBehavior() {\n return {\n y: 0\n };\n },\n routes: constantRoutes\n });\n};\n\nvar router = createRouter();\nfunction resetRouter() {\n var newRouter = createRouter();\n router.matcher = newRouter.matcher; // reset router\n}\n/* harmony default export */ __webpack_exports__[\"default\"] = (router);\n\n//# sourceURL=webpack:///./src/router/index.js?"); + +/***/ }), + +/***/ "./src/settings.js": +/*!*************************!*\ + !*** ./src/settings.js ***! + \*************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("module.exports = {\n title: '云帆考试培训系统',\n\n /**\n * @type {boolean} true | false\n * @description Whether show the settings right-panel\n */\n showSettings: false,\n\n /**\n * @type {boolean} true | false\n * @description Whether need tagsView\n */\n tagsView: true,\n\n /**\n * @type {boolean} true | false\n * @description Whether fix the header\n */\n fixedHeader: false,\n\n /**\n * @type {boolean} true | false\n * @description Whether show the logo in sidebar\n */\n sidebarLogo: true,\n\n /**\n * @type {string | array} 'production' | ['production', 'development']\n * @description Need show err logs component.\n * The default is only used in the production env\n * If you want to also use it in dev, you can pass ['production', 'development']\n */\n errorLog: 'production'\n};\n\n//# sourceURL=webpack:///./src/settings.js?"); + +/***/ }), + +/***/ "./src/store/getters.js": +/*!******************************!*\ + !*** ./src/store/getters.js ***! + \******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var core_js_modules_es6_function_name__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core-js/modules/es6.function.name */ \"./node_modules/core-js/modules/es6.function.name.js\");\n/* harmony import */ var core_js_modules_es6_function_name__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es6_function_name__WEBPACK_IMPORTED_MODULE_0__);\n\nvar getters = {\n sidebar: function sidebar(state) {\n return state.app.sidebar;\n },\n size: function size(state) {\n return state.app.size;\n },\n device: function device(state) {\n return state.app.device;\n },\n visitedViews: function visitedViews(state) {\n return state.tagsView.visitedViews;\n },\n cachedViews: function cachedViews(state) {\n return state.tagsView.cachedViews;\n },\n token: function token(state) {\n return state.user.token;\n },\n avatar: function avatar(state) {\n return state.user.avatar;\n },\n userId: function userId(state) {\n return state.user.userId;\n },\n name: function name(state) {\n return state.user.name;\n },\n realName: function realName(state) {\n return state.user.realName;\n },\n introduction: function introduction(state) {\n return state.user.introduction;\n },\n roles: function roles(state) {\n return state.user.roles;\n },\n permission_routes: function permission_routes(state) {\n return state.permission.routes;\n },\n errorLogs: function errorLogs(state) {\n return state.errorLog.logs;\n },\n siteData: function siteData(state) {\n return state.settings.siteData;\n }\n};\n/* harmony default export */ __webpack_exports__[\"default\"] = (getters);\n\n//# sourceURL=webpack:///./src/store/getters.js?"); + +/***/ }), + +/***/ "./src/store/index.js": +/*!****************************!*\ + !*** ./src/store/index.js ***! + \****************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var core_js_modules_es6_regexp_replace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core-js/modules/es6.regexp.replace */ \"./node_modules/core-js/modules/es6.regexp.replace.js\");\n/* harmony import */ var core_js_modules_es6_regexp_replace__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es6_regexp_replace__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! core-js/modules/web.dom.iterable */ \"./node_modules/core-js/modules/web.dom.iterable.js\");\n/* harmony import */ var core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.runtime.esm.js\");\n/* harmony import */ var vuex__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! vuex */ \"./node_modules/vuex/dist/vuex.esm.js\");\n/* harmony import */ var _getters__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./getters */ \"./src/store/getters.js\");\n\n\n\n\n\nvue__WEBPACK_IMPORTED_MODULE_2__[\"default\"].use(vuex__WEBPACK_IMPORTED_MODULE_3__[\"default\"]); // https://webpack.js.org/guides/dependency-management/#requirecontext\n\nvar modulesFiles = __webpack_require__(\"./src/store/modules sync recursive \\\\.js$\"); // you do not need `import app from './modules/app'`\n// it will auto require all vuex module from modules file\n\n\nvar modules = modulesFiles.keys().reduce(function (modules, modulePath) {\n // set './app.js' => 'app'\n var moduleName = modulePath.replace(/^\\.\\/(.*)\\.\\w+$/, '$1');\n var value = modulesFiles(modulePath);\n modules[moduleName] = value.default;\n return modules;\n}, {});\nvar store = new vuex__WEBPACK_IMPORTED_MODULE_3__[\"default\"].Store({\n modules: modules,\n getters: _getters__WEBPACK_IMPORTED_MODULE_4__[\"default\"]\n});\n/* harmony default export */ __webpack_exports__[\"default\"] = (store);\n\n//# sourceURL=webpack:///./src/store/index.js?"); + +/***/ }), + +/***/ "./src/store/modules sync recursive \\.js$": +/*!**************************************!*\ + !*** ./src/store/modules sync \.js$ ***! + \**************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var map = {\n\t\"./app.js\": \"./src/store/modules/app.js\",\n\t\"./errorLog.js\": \"./src/store/modules/errorLog.js\",\n\t\"./permission.js\": \"./src/store/modules/permission.js\",\n\t\"./settings.js\": \"./src/store/modules/settings.js\",\n\t\"./tagsView.js\": \"./src/store/modules/tagsView.js\",\n\t\"./user.js\": \"./src/store/modules/user.js\"\n};\n\n\nfunction webpackContext(req) {\n\tvar id = webpackContextResolve(req);\n\treturn __webpack_require__(id);\n}\nfunction webpackContextResolve(req) {\n\tif(!__webpack_require__.o(map, req)) {\n\t\tvar e = new Error(\"Cannot find module '\" + req + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\treturn map[req];\n}\nwebpackContext.keys = function webpackContextKeys() {\n\treturn Object.keys(map);\n};\nwebpackContext.resolve = webpackContextResolve;\nmodule.exports = webpackContext;\nwebpackContext.id = \"./src/store/modules sync recursive \\\\.js$\";\n\n//# sourceURL=webpack:///./src/store/modules_sync_\\.js$?"); + +/***/ }), + +/***/ "./src/store/modules/app.js": +/*!**********************************!*\ + !*** ./src/store/modules/app.js ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var js_cookie__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! js-cookie */ \"./node_modules/js-cookie/src/js.cookie.js\");\n/* harmony import */ var js_cookie__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(js_cookie__WEBPACK_IMPORTED_MODULE_0__);\n\nvar state = {\n sidebar: {\n opened: js_cookie__WEBPACK_IMPORTED_MODULE_0___default.a.get('sidebarStatus') ? !!+js_cookie__WEBPACK_IMPORTED_MODULE_0___default.a.get('sidebarStatus') : true,\n withoutAnimation: false\n },\n device: 'desktop',\n size: js_cookie__WEBPACK_IMPORTED_MODULE_0___default.a.get('size') || 'medium'\n};\nvar mutations = {\n TOGGLE_SIDEBAR: function TOGGLE_SIDEBAR(state) {\n state.sidebar.opened = !state.sidebar.opened;\n state.sidebar.withoutAnimation = false;\n\n if (state.sidebar.opened) {\n js_cookie__WEBPACK_IMPORTED_MODULE_0___default.a.set('sidebarStatus', 1);\n } else {\n js_cookie__WEBPACK_IMPORTED_MODULE_0___default.a.set('sidebarStatus', 0);\n }\n },\n CLOSE_SIDEBAR: function CLOSE_SIDEBAR(state, withoutAnimation) {\n js_cookie__WEBPACK_IMPORTED_MODULE_0___default.a.set('sidebarStatus', 0);\n state.sidebar.opened = false;\n state.sidebar.withoutAnimation = withoutAnimation;\n },\n TOGGLE_DEVICE: function TOGGLE_DEVICE(state, device) {\n state.device = device;\n },\n SET_SIZE: function SET_SIZE(state, size) {\n state.size = size;\n js_cookie__WEBPACK_IMPORTED_MODULE_0___default.a.set('size', size);\n }\n};\nvar actions = {\n toggleSideBar: function toggleSideBar(_ref) {\n var commit = _ref.commit;\n commit('TOGGLE_SIDEBAR');\n },\n closeSideBar: function closeSideBar(_ref2, _ref3) {\n var commit = _ref2.commit;\n var withoutAnimation = _ref3.withoutAnimation;\n commit('CLOSE_SIDEBAR', withoutAnimation);\n },\n toggleDevice: function toggleDevice(_ref4, device) {\n var commit = _ref4.commit;\n commit('TOGGLE_DEVICE', device);\n },\n setSize: function setSize(_ref5, size) {\n var commit = _ref5.commit;\n commit('SET_SIZE', size);\n }\n};\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n namespaced: true,\n state: state,\n mutations: mutations,\n actions: actions\n});\n\n//# sourceURL=webpack:///./src/store/modules/app.js?"); + +/***/ }), + +/***/ "./src/store/modules/errorLog.js": +/*!***************************************!*\ + !*** ./src/store/modules/errorLog.js ***! + \***************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar state = {\n logs: []\n};\nvar mutations = {\n ADD_ERROR_LOG: function ADD_ERROR_LOG(state, log) {\n state.logs.push(log);\n },\n CLEAR_ERROR_LOG: function CLEAR_ERROR_LOG(state) {\n state.logs.splice(0);\n }\n};\nvar actions = {\n addErrorLog: function addErrorLog(_ref, log) {\n var commit = _ref.commit;\n commit('ADD_ERROR_LOG', log);\n },\n clearErrorLog: function clearErrorLog(_ref2) {\n var commit = _ref2.commit;\n commit('CLEAR_ERROR_LOG');\n }\n};\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n namespaced: true,\n state: state,\n mutations: mutations,\n actions: actions\n});\n\n//# sourceURL=webpack:///./src/store/modules/errorLog.js?"); + +/***/ }), + +/***/ "./src/store/modules/permission.js": +/*!*****************************************!*\ + !*** ./src/store/modules/permission.js ***! + \*****************************************/ +/*! exports provided: filterAsyncRoutes, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"filterAsyncRoutes\", function() { return filterAsyncRoutes; });\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./node_modules/@babel/runtime-corejs2/helpers/esm/objectSpread2.js */ \"./node_modules/@babel/runtime-corejs2/helpers/esm/objectSpread2.js\");\n/* harmony import */ var core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! core-js/modules/web.dom.iterable */ \"./node_modules/core-js/modules/web.dom.iterable.js\");\n/* harmony import */ var core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var core_js_modules_es7_array_includes__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! core-js/modules/es7.array.includes */ \"./node_modules/core-js/modules/es7.array.includes.js\");\n/* harmony import */ var core_js_modules_es7_array_includes__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es7_array_includes__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var core_js_modules_es6_string_includes__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! core-js/modules/es6.string.includes */ \"./node_modules/core-js/modules/es6.string.includes.js\");\n/* harmony import */ var core_js_modules_es6_string_includes__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es6_string_includes__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _router__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @/router */ \"./src/router/index.js\");\n\n\n\n\n\n/**\n * Use meta.role to determine if the current user has permission\n * @param roles\n * @param route\n */\n\nfunction hasPermission(roles, route) {\n if (route.meta && route.meta.roles) {\n return roles.some(function (role) {\n return route.meta.roles.includes(role);\n });\n } else {\n return true;\n }\n}\n/**\n * Filter asynchronous routing tables by recursion\n * @param routes asyncRoutes\n * @param roles\n */\n\n\nfunction filterAsyncRoutes(routes, roles) {\n var res = [];\n routes.forEach(function (route) {\n var tmp = Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, route);\n\n if (hasPermission(roles, tmp)) {\n if (tmp.children) {\n tmp.children = filterAsyncRoutes(tmp.children, roles);\n }\n\n res.push(tmp);\n }\n });\n return res;\n}\nvar state = {\n routes: [],\n addRoutes: []\n};\nvar mutations = {\n SET_ROUTES: function SET_ROUTES(state, routes) {\n state.addRoutes = routes;\n state.routes = _router__WEBPACK_IMPORTED_MODULE_4__[\"constantRoutes\"].concat(routes);\n }\n};\nvar actions = {\n generateRoutes: function generateRoutes(_ref, roles) {\n var commit = _ref.commit;\n return new Promise(function (resolve) {\n var accessedRoutes = filterAsyncRoutes(_router__WEBPACK_IMPORTED_MODULE_4__[\"asyncRoutes\"], roles);\n commit('SET_ROUTES', accessedRoutes);\n resolve(accessedRoutes);\n });\n }\n};\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n namespaced: true,\n state: state,\n mutations: mutations,\n actions: actions\n});\n\n//# sourceURL=webpack:///./src/store/modules/permission.js?"); + +/***/ }), + +/***/ "./src/store/modules/settings.js": +/*!***************************************!*\ + !*** ./src/store/modules/settings.js ***! + \***************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _styles_element_variables_scss__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @/styles/element-variables.scss */ \"./src/styles/element-variables.scss\");\n/* harmony import */ var _styles_element_variables_scss__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_styles_element_variables_scss__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _settings__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @/settings */ \"./src/settings.js\");\n/* harmony import */ var _settings__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_settings__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _api_sys_config_config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @/api/sys/config/config */ \"./src/api/sys/config/config.js\");\n\n\n\nvar showSettings = _settings__WEBPACK_IMPORTED_MODULE_1___default.a.showSettings,\n tagsView = _settings__WEBPACK_IMPORTED_MODULE_1___default.a.tagsView,\n fixedHeader = _settings__WEBPACK_IMPORTED_MODULE_1___default.a.fixedHeader,\n sidebarLogo = _settings__WEBPACK_IMPORTED_MODULE_1___default.a.sidebarLogo;\nvar state = {\n theme: _styles_element_variables_scss__WEBPACK_IMPORTED_MODULE_0___default.a.theme,\n showSettings: showSettings,\n tagsView: tagsView,\n fixedHeader: fixedHeader,\n sidebarLogo: sidebarLogo,\n siteData: {}\n};\nvar mutations = {\n CHANGE_SETTING: function CHANGE_SETTING(state, _ref) {\n var key = _ref.key,\n value = _ref.value;\n\n if (state.hasOwnProperty(key)) {\n state[key] = value;\n }\n },\n SET_SITE_DATA: function SET_SITE_DATA(state, siteData) {\n state.siteData = siteData;\n }\n};\nvar actions = {\n changeSetting: function changeSetting(_ref2, data) {\n var commit = _ref2.commit;\n commit('CHANGE_SETTING', data);\n },\n // 获取网站配置信息\n getSite: function getSite(_ref3) {\n var commit = _ref3.commit;\n return new Promise(function (resolve, reject) {\n Object(_api_sys_config_config__WEBPACK_IMPORTED_MODULE_2__[\"fetchDetail\"])({}).then(function (response) {\n var data = response.data;\n commit('SET_SITE_DATA', data);\n resolve(data);\n }).catch(function (error) {\n reject(error);\n });\n });\n }\n};\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n namespaced: true,\n state: state,\n mutations: mutations,\n actions: actions\n});\n\n//# sourceURL=webpack:///./src/store/modules/settings.js?"); + +/***/ }), + +/***/ "./src/store/modules/tagsView.js": +/*!***************************************!*\ + !*** ./src/store/modules/tagsView.js ***! + \***************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./node_modules/@babel/runtime-corejs2/helpers/esm/toConsumableArray.js */ \"./node_modules/@babel/runtime-corejs2/helpers/esm/toConsumableArray.js\");\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_slicedToArray_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./node_modules/@babel/runtime-corejs2/helpers/esm/slicedToArray.js */ \"./node_modules/@babel/runtime-corejs2/helpers/esm/slicedToArray.js\");\n/* harmony import */ var core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! core-js/modules/web.dom.iterable */ \"./node_modules/core-js/modules/web.dom.iterable.js\");\n/* harmony import */ var core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_web_dom_iterable__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_createForOfIteratorHelper_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./node_modules/@babel/runtime-corejs2/helpers/esm/createForOfIteratorHelper.js */ \"./node_modules/@babel/runtime-corejs2/helpers/esm/createForOfIteratorHelper.js\");\n/* harmony import */ var core_js_modules_es6_function_name__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! core-js/modules/es6.function.name */ \"./node_modules/core-js/modules/es6.function.name.js\");\n/* harmony import */ var core_js_modules_es6_function_name__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es6_function_name__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var core_js_modules_es7_array_includes__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! core-js/modules/es7.array.includes */ \"./node_modules/core-js/modules/es7.array.includes.js\");\n/* harmony import */ var core_js_modules_es7_array_includes__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es7_array_includes__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var core_js_modules_es6_string_includes__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! core-js/modules/es6.string.includes */ \"./node_modules/core-js/modules/es6.string.includes.js\");\n/* harmony import */ var core_js_modules_es6_string_includes__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es6_string_includes__WEBPACK_IMPORTED_MODULE_6__);\n\n\n\n\n\n\n\nvar state = {\n visitedViews: [],\n cachedViews: []\n};\nvar mutations = {\n ADD_VISITED_VIEW: function ADD_VISITED_VIEW(state, view) {\n if (state.visitedViews.some(function (v) {\n return v.path === view.path;\n })) return;\n state.visitedViews.push(Object.assign({}, view, {\n title: view.meta.title || 'no-name'\n }));\n },\n ADD_CACHED_VIEW: function ADD_CACHED_VIEW(state, view) {\n if (state.cachedViews.includes(view.name)) return;\n\n if (!view.meta.noCache) {\n state.cachedViews.push(view.name);\n }\n },\n DEL_VISITED_VIEW: function DEL_VISITED_VIEW(state, view) {\n var _iterator = Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_createForOfIteratorHelper_js__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(state.visitedViews.entries()),\n _step;\n\n try {\n for (_iterator.s(); !(_step = _iterator.n()).done;) {\n var _step$value = Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_slicedToArray_js__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(_step.value, 2),\n i = _step$value[0],\n v = _step$value[1];\n\n if (v.path === view.path) {\n state.visitedViews.splice(i, 1);\n break;\n }\n }\n } catch (err) {\n _iterator.e(err);\n } finally {\n _iterator.f();\n }\n },\n DEL_CACHED_VIEW: function DEL_CACHED_VIEW(state, view) {\n var index = state.cachedViews.indexOf(view.name);\n index > -1 && state.cachedViews.splice(index, 1);\n },\n DEL_OTHERS_VISITED_VIEWS: function DEL_OTHERS_VISITED_VIEWS(state, view) {\n state.visitedViews = state.visitedViews.filter(function (v) {\n return v.meta.affix || v.path === view.path;\n });\n },\n DEL_OTHERS_CACHED_VIEWS: function DEL_OTHERS_CACHED_VIEWS(state, view) {\n var index = state.cachedViews.indexOf(view.name);\n\n if (index > -1) {\n state.cachedViews = state.cachedViews.slice(index, index + 1);\n } else {\n // if index = -1, there is no cached tags\n state.cachedViews = [];\n }\n },\n DEL_ALL_VISITED_VIEWS: function DEL_ALL_VISITED_VIEWS(state) {\n // keep affix tags\n var affixTags = state.visitedViews.filter(function (tag) {\n return tag.meta.affix;\n });\n state.visitedViews = affixTags;\n },\n DEL_ALL_CACHED_VIEWS: function DEL_ALL_CACHED_VIEWS(state) {\n state.cachedViews = [];\n },\n UPDATE_VISITED_VIEW: function UPDATE_VISITED_VIEW(state, view) {\n var _iterator2 = Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_createForOfIteratorHelper_js__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(state.visitedViews),\n _step2;\n\n try {\n for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {\n var v = _step2.value;\n\n if (v.path === view.path) {\n v = Object.assign(v, view);\n break;\n }\n }\n } catch (err) {\n _iterator2.e(err);\n } finally {\n _iterator2.f();\n }\n }\n};\nvar actions = {\n addView: function addView(_ref, view) {\n var dispatch = _ref.dispatch;\n dispatch('addVisitedView', view);\n dispatch('addCachedView', view);\n },\n addVisitedView: function addVisitedView(_ref2, view) {\n var commit = _ref2.commit;\n commit('ADD_VISITED_VIEW', view);\n },\n addCachedView: function addCachedView(_ref3, view) {\n var commit = _ref3.commit;\n commit('ADD_CACHED_VIEW', view);\n },\n delView: function delView(_ref4, view) {\n var dispatch = _ref4.dispatch,\n state = _ref4.state;\n return new Promise(function (resolve) {\n dispatch('delVisitedView', view);\n dispatch('delCachedView', view);\n resolve({\n visitedViews: Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.visitedViews),\n cachedViews: Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.cachedViews)\n });\n });\n },\n delVisitedView: function delVisitedView(_ref5, view) {\n var commit = _ref5.commit,\n state = _ref5.state;\n return new Promise(function (resolve) {\n commit('DEL_VISITED_VIEW', view);\n resolve(Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.visitedViews));\n });\n },\n delCachedView: function delCachedView(_ref6, view) {\n var commit = _ref6.commit,\n state = _ref6.state;\n return new Promise(function (resolve) {\n commit('DEL_CACHED_VIEW', view);\n resolve(Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.cachedViews));\n });\n },\n delOthersViews: function delOthersViews(_ref7, view) {\n var dispatch = _ref7.dispatch,\n state = _ref7.state;\n return new Promise(function (resolve) {\n dispatch('delOthersVisitedViews', view);\n dispatch('delOthersCachedViews', view);\n resolve({\n visitedViews: Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.visitedViews),\n cachedViews: Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.cachedViews)\n });\n });\n },\n delOthersVisitedViews: function delOthersVisitedViews(_ref8, view) {\n var commit = _ref8.commit,\n state = _ref8.state;\n return new Promise(function (resolve) {\n commit('DEL_OTHERS_VISITED_VIEWS', view);\n resolve(Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.visitedViews));\n });\n },\n delOthersCachedViews: function delOthersCachedViews(_ref9, view) {\n var commit = _ref9.commit,\n state = _ref9.state;\n return new Promise(function (resolve) {\n commit('DEL_OTHERS_CACHED_VIEWS', view);\n resolve(Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.cachedViews));\n });\n },\n delAllViews: function delAllViews(_ref10, view) {\n var dispatch = _ref10.dispatch,\n state = _ref10.state;\n return new Promise(function (resolve) {\n dispatch('delAllVisitedViews', view);\n dispatch('delAllCachedViews', view);\n resolve({\n visitedViews: Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.visitedViews),\n cachedViews: Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.cachedViews)\n });\n });\n },\n delAllVisitedViews: function delAllVisitedViews(_ref11) {\n var commit = _ref11.commit,\n state = _ref11.state;\n return new Promise(function (resolve) {\n commit('DEL_ALL_VISITED_VIEWS');\n resolve(Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.visitedViews));\n });\n },\n delAllCachedViews: function delAllCachedViews(_ref12) {\n var commit = _ref12.commit,\n state = _ref12.state;\n return new Promise(function (resolve) {\n commit('DEL_ALL_CACHED_VIEWS');\n resolve(Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(state.cachedViews));\n });\n },\n updateVisitedView: function updateVisitedView(_ref13, view) {\n var commit = _ref13.commit;\n commit('UPDATE_VISITED_VIEW', view);\n }\n};\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n namespaced: true,\n state: state,\n mutations: mutations,\n actions: actions\n});\n\n//# sourceURL=webpack:///./src/store/modules/tagsView.js?"); + +/***/ }), + +/***/ "./src/store/modules/user.js": +/*!***********************************!*\ + !*** ./src/store/modules/user.js ***! + \***********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var regenerator_runtime_runtime__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! regenerator-runtime/runtime */ \"./node_modules/regenerator-runtime/runtime.js\");\n/* harmony import */ var regenerator_runtime_runtime__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(regenerator_runtime_runtime__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./node_modules/@babel/runtime-corejs2/helpers/esm/asyncToGenerator.js */ \"./node_modules/@babel/runtime-corejs2/helpers/esm/asyncToGenerator.js\");\n/* harmony import */ var _api_user__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @/api/user */ \"./src/api/user.js\");\n/* harmony import */ var _utils_auth__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @/utils/auth */ \"./src/utils/auth.js\");\n/* harmony import */ var _router__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @/router */ \"./src/router/index.js\");\n\n\n\n\n\nvar state = {\n token: Object(_utils_auth__WEBPACK_IMPORTED_MODULE_3__[\"getToken\"])(),\n userId: '',\n name: '',\n realName: '',\n avatar: '',\n introduction: '',\n roles: []\n};\nvar mutations = {\n SET_TOKEN: function SET_TOKEN(state, token) {\n state.token = token;\n },\n SET_INTRODUCTION: function SET_INTRODUCTION(state, introduction) {\n state.introduction = introduction;\n },\n SET_ID: function SET_ID(state, userId) {\n state.userId = userId;\n },\n SET_NAME: function SET_NAME(state, name) {\n state.name = name;\n },\n SET_REAL_NAME: function SET_REAL_NAME(state, realName) {\n state.realName = realName;\n },\n SET_AVATAR: function SET_AVATAR(state, avatar) {\n state.avatar = avatar;\n },\n SET_ROLES: function SET_ROLES(state, roles) {\n state.roles = roles;\n }\n};\nvar actions = {\n // user login\n login: function login(_ref, userInfo) {\n var commit = _ref.commit;\n var username = userInfo.username,\n password = userInfo.password;\n return new Promise(function (resolve, reject) {\n Object(_api_user__WEBPACK_IMPORTED_MODULE_2__[\"login\"])({\n username: username.trim(),\n password: password\n }).then(function (response) {\n var data = response.data;\n commit('SET_TOKEN', data.token);\n Object(_utils_auth__WEBPACK_IMPORTED_MODULE_3__[\"setToken\"])(data.token);\n resolve();\n }).catch(function (error) {\n reject(error);\n });\n });\n },\n reg: function reg(_ref2, userInfo) {\n var commit = _ref2.commit;\n var userName = userInfo.userName,\n realName = userInfo.realName,\n password = userInfo.password;\n return new Promise(function (resolve, reject) {\n Object(_api_user__WEBPACK_IMPORTED_MODULE_2__[\"reg\"])({\n userName: userName.trim(),\n realName: realName.trim(),\n password: password\n }).then(function (response) {\n var data = response.data;\n commit('SET_TOKEN', data.token);\n Object(_utils_auth__WEBPACK_IMPORTED_MODULE_3__[\"setToken\"])(data.token);\n resolve();\n }).catch(function (error) {\n reject(error);\n });\n });\n },\n // get user info\n getInfo: function getInfo(_ref3) {\n var commit = _ref3.commit,\n state = _ref3.state;\n return new Promise(function (resolve, reject) {\n Object(_api_user__WEBPACK_IMPORTED_MODULE_2__[\"getInfo\"])(state.token).then(function (response) {\n var data = response.data;\n\n if (!data) {\n reject('校验失败,请重新登录!.');\n }\n\n var id = data.id,\n roles = data.roles,\n userName = data.userName,\n realName = data.realName,\n avatar = data.avatar,\n introduction = data.introduction; // roles must be a non-empty array\n\n if (!roles || roles.length <= 0) {\n reject('用户角色不能为空!');\n }\n\n commit('SET_ID', id);\n commit('SET_ROLES', roles);\n commit('SET_REAL_NAME', realName);\n commit('SET_NAME', userName);\n commit('SET_AVATAR', avatar);\n commit('SET_INTRODUCTION', introduction);\n resolve(data);\n }).catch(function (error) {\n reject(error);\n });\n });\n },\n // user logout\n logout: function logout(_ref4) {\n var commit = _ref4.commit,\n state = _ref4.state,\n dispatch = _ref4.dispatch;\n return new Promise(function (resolve, reject) {\n Object(_api_user__WEBPACK_IMPORTED_MODULE_2__[\"logout\"])(state.token).then(function () {\n commit('SET_TOKEN', '');\n commit('SET_ROLES', []);\n Object(_utils_auth__WEBPACK_IMPORTED_MODULE_3__[\"removeToken\"])();\n Object(_router__WEBPACK_IMPORTED_MODULE_4__[\"resetRouter\"])(); // reset visited views and cached views\n // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485\n\n dispatch('tagsView/delAllViews', null, {\n root: true\n });\n resolve();\n }).catch(function (error) {\n reject(error);\n });\n });\n },\n // remove token\n resetToken: function resetToken(_ref5) {\n var commit = _ref5.commit;\n return new Promise(function (resolve) {\n commit('SET_TOKEN', '');\n commit('SET_ROLES', []);\n Object(_utils_auth__WEBPACK_IMPORTED_MODULE_3__[\"removeToken\"])();\n resolve();\n });\n },\n // dynamically modify permissions\n changeRoles: function changeRoles(_ref6, role) {\n var commit = _ref6.commit,\n dispatch = _ref6.dispatch;\n return new Promise( /*#__PURE__*/function () {\n var _ref7 = Object(_Users_van_Documents_yf_projects_yf_exam_lite_exam_vue_node_modules_babel_runtime_corejs2_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_1__[\"default\"])( /*#__PURE__*/regeneratorRuntime.mark(function _callee(resolve) {\n var token, _yield$dispatch, roles, accessRoutes;\n\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n token = role + '-token';\n commit('SET_TOKEN', token);\n Object(_utils_auth__WEBPACK_IMPORTED_MODULE_3__[\"setToken\"])(token);\n _context.next = 5;\n return dispatch('getInfo');\n\n case 5:\n _yield$dispatch = _context.sent;\n roles = _yield$dispatch.roles;\n Object(_router__WEBPACK_IMPORTED_MODULE_4__[\"resetRouter\"])(); // generate accessible routes map based on roles\n\n _context.next = 10;\n return dispatch('permission/generateRoutes', roles, {\n root: true\n });\n\n case 10:\n accessRoutes = _context.sent;\n // dynamically add accessible routes\n _router__WEBPACK_IMPORTED_MODULE_4__[\"default\"].addRoutes(accessRoutes); // reset visited views and cached views\n\n dispatch('tagsView/delAllViews', null, {\n root: true\n });\n resolve();\n\n case 14:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee);\n }));\n\n return function (_x) {\n return _ref7.apply(this, arguments);\n };\n }());\n }\n};\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n namespaced: true,\n state: state,\n mutations: mutations,\n actions: actions\n});\n\n//# sourceURL=webpack:///./src/store/modules/user.js?"); + +/***/ }), + +/***/ "./src/styles/element-variables.scss": +/*!*******************************************!*\ + !*** ./src/styles/element-variables.scss ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("// style-loader: Adds some css to the DOM by adding a diff --git a/exam-vue/src/components/Breadcrumb/index.vue b/exam-vue/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..cc2ed81 --- /dev/null +++ b/exam-vue/src/components/Breadcrumb/index.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/exam-vue/src/components/DataTable/index.vue b/exam-vue/src/components/DataTable/index.vue new file mode 100644 index 0000000..69e81da --- /dev/null +++ b/exam-vue/src/components/DataTable/index.vue @@ -0,0 +1,262 @@ + + + + + diff --git a/exam-vue/src/components/DepartTreeSelect/index.vue b/exam-vue/src/components/DepartTreeSelect/index.vue new file mode 100644 index 0000000..97e07c6 --- /dev/null +++ b/exam-vue/src/components/DepartTreeSelect/index.vue @@ -0,0 +1,226 @@ + + + + + + diff --git a/exam-vue/src/components/ExamSelect/index.vue b/exam-vue/src/components/ExamSelect/index.vue new file mode 100644 index 0000000..4c7cab2 --- /dev/null +++ b/exam-vue/src/components/ExamSelect/index.vue @@ -0,0 +1,70 @@ + + + diff --git a/exam-vue/src/components/FileUpload/index.vue b/exam-vue/src/components/FileUpload/index.vue new file mode 100644 index 0000000..f400951 --- /dev/null +++ b/exam-vue/src/components/FileUpload/index.vue @@ -0,0 +1,63 @@ + + + diff --git a/exam-vue/src/components/FileUpload/local.vue b/exam-vue/src/components/FileUpload/local.vue new file mode 100644 index 0000000..7e28d87 --- /dev/null +++ b/exam-vue/src/components/FileUpload/local.vue @@ -0,0 +1,113 @@ + + + diff --git a/exam-vue/src/components/Hamburger/index.vue b/exam-vue/src/components/Hamburger/index.vue new file mode 100644 index 0000000..368b002 --- /dev/null +++ b/exam-vue/src/components/Hamburger/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/exam-vue/src/components/HeaderSearch/index.vue b/exam-vue/src/components/HeaderSearch/index.vue new file mode 100644 index 0000000..6026ebb --- /dev/null +++ b/exam-vue/src/components/HeaderSearch/index.vue @@ -0,0 +1,180 @@ + + + + + diff --git a/exam-vue/src/components/MeetRole/index.vue b/exam-vue/src/components/MeetRole/index.vue new file mode 100644 index 0000000..96542f1 --- /dev/null +++ b/exam-vue/src/components/MeetRole/index.vue @@ -0,0 +1,70 @@ + + + diff --git a/exam-vue/src/components/Pagination/index.vue b/exam-vue/src/components/Pagination/index.vue new file mode 100644 index 0000000..c815e13 --- /dev/null +++ b/exam-vue/src/components/Pagination/index.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/exam-vue/src/components/PanThumb/index.vue b/exam-vue/src/components/PanThumb/index.vue new file mode 100644 index 0000000..1bcf417 --- /dev/null +++ b/exam-vue/src/components/PanThumb/index.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/exam-vue/src/components/RepoSelect/index.vue b/exam-vue/src/components/RepoSelect/index.vue new file mode 100644 index 0000000..4b1f1b2 --- /dev/null +++ b/exam-vue/src/components/RepoSelect/index.vue @@ -0,0 +1,77 @@ + + + diff --git a/exam-vue/src/components/RightPanel/index.vue b/exam-vue/src/components/RightPanel/index.vue new file mode 100644 index 0000000..e911521 --- /dev/null +++ b/exam-vue/src/components/RightPanel/index.vue @@ -0,0 +1,145 @@ + + + + + + + diff --git a/exam-vue/src/components/Screenfull/index.vue b/exam-vue/src/components/Screenfull/index.vue new file mode 100644 index 0000000..260c90d --- /dev/null +++ b/exam-vue/src/components/Screenfull/index.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/exam-vue/src/components/SizeSelect/index.vue b/exam-vue/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..3fe7417 --- /dev/null +++ b/exam-vue/src/components/SizeSelect/index.vue @@ -0,0 +1,57 @@ + + + diff --git a/exam-vue/src/components/SvgIcon/index.vue b/exam-vue/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..9a3318e --- /dev/null +++ b/exam-vue/src/components/SvgIcon/index.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/exam-vue/src/components/ThemePicker/index.vue b/exam-vue/src/components/ThemePicker/index.vue new file mode 100644 index 0000000..3879c5a --- /dev/null +++ b/exam-vue/src/components/ThemePicker/index.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/exam-vue/src/directive/clipboard/clipboard.js b/exam-vue/src/directive/clipboard/clipboard.js new file mode 100644 index 0000000..514aad2 --- /dev/null +++ b/exam-vue/src/directive/clipboard/clipboard.js @@ -0,0 +1,49 @@ +// Inspired by https://github.com/Inndy/vue-clipboard2 +const Clipboard = require('clipboard') +if (!Clipboard) { + throw new Error('you should npm install `clipboard` --save at first ') +} + +export default { + bind(el, binding) { + if (binding.arg === 'success') { + el._v_clipboard_success = binding.value + } else if (binding.arg === 'error') { + el._v_clipboard_error = binding.value + } else { + const clipboard = new Clipboard(el, { + text() { return binding.value }, + action() { return binding.arg === 'cut' ? 'cut' : 'copy' } + }) + clipboard.on('success', e => { + const callback = el._v_clipboard_success + callback && callback(e) // eslint-disable-line + }) + clipboard.on('error', e => { + const callback = el._v_clipboard_error + callback && callback(e) // eslint-disable-line + }) + el._v_clipboard = clipboard + } + }, + update(el, binding) { + if (binding.arg === 'success') { + el._v_clipboard_success = binding.value + } else if (binding.arg === 'error') { + el._v_clipboard_error = binding.value + } else { + el._v_clipboard.text = function() { return binding.value } + el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' } + } + }, + unbind(el, binding) { + if (binding.arg === 'success') { + delete el._v_clipboard_success + } else if (binding.arg === 'error') { + delete el._v_clipboard_error + } else { + el._v_clipboard.destroy() + delete el._v_clipboard + } + } +} diff --git a/exam-vue/src/directive/clipboard/index.js b/exam-vue/src/directive/clipboard/index.js new file mode 100644 index 0000000..02c9816 --- /dev/null +++ b/exam-vue/src/directive/clipboard/index.js @@ -0,0 +1,13 @@ +import Clipboard from './clipboard' + +const install = function(Vue) { + Vue.directive('Clipboard', Clipboard) +} + +if (window.Vue) { + window.clipboard = Clipboard + Vue.use(install); // eslint-disable-line +} + +Clipboard.install = install +export default Clipboard diff --git a/exam-vue/src/directive/permission/index.js b/exam-vue/src/directive/permission/index.js new file mode 100644 index 0000000..e5dadd3 --- /dev/null +++ b/exam-vue/src/directive/permission/index.js @@ -0,0 +1,13 @@ +import permission from './permission' + +const install = function(Vue) { + Vue.directive('permission', permission) +} + +if (window.Vue) { + window['permission'] = permission + Vue.use(install); // eslint-disable-line +} + +permission.install = install +export default permission diff --git a/exam-vue/src/directive/permission/permission.js b/exam-vue/src/directive/permission/permission.js new file mode 100644 index 0000000..b27f24d --- /dev/null +++ b/exam-vue/src/directive/permission/permission.js @@ -0,0 +1,22 @@ +import store from '@/store' + +export default { + inserted(el, binding, vnode) { + const { value } = binding + const roles = store.getters && store.getters.roles + + if (value && value instanceof Array && value.length > 0) { + const permissionRoles = value + + const hasPermission = roles.some(role => { + return permissionRoles.includes(role) + }) + + if (!hasPermission) { + el.parentNode && el.parentNode.removeChild(el) + } + } else { + throw new Error(`need roles! Like v-permission="['admin','editor']"`) + } + } +} diff --git a/exam-vue/src/directive/sticky.js b/exam-vue/src/directive/sticky.js new file mode 100644 index 0000000..bc23466 --- /dev/null +++ b/exam-vue/src/directive/sticky.js @@ -0,0 +1,91 @@ +const vueSticky = {} +let listenAction +vueSticky.install = Vue => { + Vue.directive('sticky', { + inserted(el, binding) { + const params = binding.value || {} + const stickyTop = params.stickyTop || 0 + const zIndex = params.zIndex || 1000 + const elStyle = el.style + + elStyle.position = '-webkit-sticky' + elStyle.position = 'sticky' + // if the browser support css sticky(Currently Safari, Firefox and Chrome Canary) + // if (~elStyle.position.indexOf('sticky')) { + // elStyle.top = `${stickyTop}px`; + // elStyle.zIndex = zIndex; + // return + // } + const elHeight = el.getBoundingClientRect().height + const elWidth = el.getBoundingClientRect().width + elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}` + + const parentElm = el.parentNode || document.documentElement + const placeholder = document.createElement('div') + placeholder.style.display = 'none' + placeholder.style.width = `${elWidth}px` + placeholder.style.height = `${elHeight}px` + parentElm.insertBefore(placeholder, el) + + let active = false + + const getScroll = (target, top) => { + const prop = top ? 'pageYOffset' : 'pageXOffset' + const method = top ? 'scrollTop' : 'scrollLeft' + let ret = target[prop] + if (typeof ret !== 'number') { + ret = window.document.documentElement[method] + } + return ret + } + + const sticky = () => { + if (active) { + return + } + if (!elStyle.height) { + elStyle.height = `${el.offsetHeight}px` + } + + elStyle.position = 'fixed' + elStyle.width = `${elWidth}px` + placeholder.style.display = 'inline-block' + active = true + } + + const reset = () => { + if (!active) { + return + } + + elStyle.position = '' + placeholder.style.display = 'none' + active = false + } + + const check = () => { + const scrollTop = getScroll(window, true) + const offsetTop = el.getBoundingClientRect().top + if (offsetTop < stickyTop) { + sticky() + } else { + if (scrollTop < elHeight + stickyTop) { + reset() + } + } + } + listenAction = () => { + check() + } + + window.addEventListener('scroll', listenAction) + }, + + unbind() { + window.removeEventListener('scroll', listenAction) + } + }) +} + +export default vueSticky + diff --git a/exam-vue/src/directive/waves/index.js b/exam-vue/src/directive/waves/index.js new file mode 100644 index 0000000..65f9b30 --- /dev/null +++ b/exam-vue/src/directive/waves/index.js @@ -0,0 +1,13 @@ +import waves from './waves' + +const install = function(Vue) { + Vue.directive('waves', waves) +} + +if (window.Vue) { + window.waves = waves + Vue.use(install); // eslint-disable-line +} + +waves.install = install +export default waves diff --git a/exam-vue/src/directive/waves/waves.css b/exam-vue/src/directive/waves/waves.css new file mode 100644 index 0000000..af7a7ef --- /dev/null +++ b/exam-vue/src/directive/waves/waves.css @@ -0,0 +1,26 @@ +.waves-ripple { + position: absolute; + border-radius: 100%; + background-color: rgba(0, 0, 0, 0.15); + background-clip: padding-box; + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-transform: scale(0); + -ms-transform: scale(0); + transform: scale(0); + opacity: 1; +} + +.waves-ripple.z-active { + opacity: 0; + -webkit-transform: scale(2); + -ms-transform: scale(2); + transform: scale(2); + -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; + transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; + transition: opacity 1.2s ease-out, transform 0.6s ease-out; + transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; +} \ No newline at end of file diff --git a/exam-vue/src/directive/waves/waves.js b/exam-vue/src/directive/waves/waves.js new file mode 100644 index 0000000..ec2ff43 --- /dev/null +++ b/exam-vue/src/directive/waves/waves.js @@ -0,0 +1,72 @@ +import './waves.css' + +const context = '@@wavesContext' + +function handleClick(el, binding) { + function handle(e) { + const customOpts = Object.assign({}, binding.value) + const opts = Object.assign({ + ele: el, // 波纹作用元素 + type: 'hit', // hit 点击位置扩散 center中心点扩展 + color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 + }, + customOpts + ) + const target = opts.ele + if (target) { + target.style.position = 'relative' + target.style.overflow = 'hidden' + const rect = target.getBoundingClientRect() + let ripple = target.querySelector('.waves-ripple') + if (!ripple) { + ripple = document.createElement('span') + ripple.className = 'waves-ripple' + ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' + target.appendChild(ripple) + } else { + ripple.className = 'waves-ripple' + } + switch (opts.type) { + case 'center': + ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px' + ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px' + break + default: + ripple.style.top = + (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || + document.body.scrollTop) + 'px' + ripple.style.left = + (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || + document.body.scrollLeft) + 'px' + } + ripple.style.backgroundColor = opts.color + ripple.className = 'waves-ripple z-active' + return false + } + } + + if (!el[context]) { + el[context] = { + removeHandle: handle + } + } else { + el[context].removeHandle = handle + } + + return handle +} + +export default { + bind(el, binding) { + el.addEventListener('click', handleClick(el, binding), false) + }, + update(el, binding) { + el.removeEventListener('click', el[context].removeHandle, false) + el.addEventListener('click', handleClick(el, binding), false) + }, + unbind(el) { + el.removeEventListener('click', el[context].removeHandle, false) + el[context] = null + delete el[context] + } +} diff --git a/exam-vue/src/filters/index.js b/exam-vue/src/filters/index.js new file mode 100644 index 0000000..ae4fd6b --- /dev/null +++ b/exam-vue/src/filters/index.js @@ -0,0 +1,60 @@ +// import parseTime, formatTime and set to filter +export { parseTime, formatTime } from '@/utils' + +/** + * Upper case first char + * @param {String} string + */ +export function uppercaseFirst(string) { + return string.charAt(0).toUpperCase() + string.slice(1) +} + +/** + * 通用状态过滤器 + * @param value + * @returns {*} + */ +export function stateFilter(value) { + const map = { + '0': '正常', + '1': '禁用' + } + return map[value] +} + +export function quTypeFilter(value) { + const map = { + '1': '单选题', + '2': '多选题', + '3': '判断题' + } + return map[value] +} + +export function paperStateFilter(value) { + const map = { + '0': '考试中', + '1': '待阅卷', + '2': '已考完', + '3': '!已弃考' + } + return map[value] +} + +export function examOpenType(value) { + const map = { + '1': '完全公开', + '2': '指定部门' + } + return map[value] +} + +export function examStateFilter(value) { + const map = { + '0': '进行中', + '1': '已禁用', + '2': '待开始', + '3': '已结束' + } + return map[value] +} diff --git a/exam-vue/src/icons/index.js b/exam-vue/src/icons/index.js new file mode 100644 index 0000000..2c6b309 --- /dev/null +++ b/exam-vue/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/exam-vue/src/icons/svg/404.svg b/exam-vue/src/icons/svg/404.svg new file mode 100644 index 0000000..6df5019 --- /dev/null +++ b/exam-vue/src/icons/svg/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/admin.svg b/exam-vue/src/icons/svg/admin.svg new file mode 100644 index 0000000..ffda10e --- /dev/null +++ b/exam-vue/src/icons/svg/admin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/agreement.svg b/exam-vue/src/icons/svg/agreement.svg new file mode 100644 index 0000000..44939c1 --- /dev/null +++ b/exam-vue/src/icons/svg/agreement.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/bug.svg b/exam-vue/src/icons/svg/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/exam-vue/src/icons/svg/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/chart.svg b/exam-vue/src/icons/svg/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/exam-vue/src/icons/svg/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/clipboard.svg b/exam-vue/src/icons/svg/clipboard.svg new file mode 100644 index 0000000..90923ff --- /dev/null +++ b/exam-vue/src/icons/svg/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/component.svg b/exam-vue/src/icons/svg/component.svg new file mode 100644 index 0000000..207ada3 --- /dev/null +++ b/exam-vue/src/icons/svg/component.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/configure.svg b/exam-vue/src/icons/svg/configure.svg new file mode 100644 index 0000000..1cd72c2 --- /dev/null +++ b/exam-vue/src/icons/svg/configure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/dashboard.svg b/exam-vue/src/icons/svg/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/exam-vue/src/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/documentation.svg b/exam-vue/src/icons/svg/documentation.svg new file mode 100644 index 0000000..7043122 --- /dev/null +++ b/exam-vue/src/icons/svg/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/drag.svg b/exam-vue/src/icons/svg/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/exam-vue/src/icons/svg/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/edit.svg b/exam-vue/src/icons/svg/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/exam-vue/src/icons/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/education.svg b/exam-vue/src/icons/svg/education.svg new file mode 100644 index 0000000..7bfb01d --- /dev/null +++ b/exam-vue/src/icons/svg/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/email.svg b/exam-vue/src/icons/svg/email.svg new file mode 100644 index 0000000..74d25e2 --- /dev/null +++ b/exam-vue/src/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/example.svg b/exam-vue/src/icons/svg/example.svg new file mode 100644 index 0000000..46f42b5 --- /dev/null +++ b/exam-vue/src/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/excel.svg b/exam-vue/src/icons/svg/excel.svg new file mode 100644 index 0000000..74d97b8 --- /dev/null +++ b/exam-vue/src/icons/svg/excel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/exit-fullscreen.svg b/exam-vue/src/icons/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/exam-vue/src/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/eye-open.svg b/exam-vue/src/icons/svg/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/exam-vue/src/icons/svg/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/eye.svg b/exam-vue/src/icons/svg/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/exam-vue/src/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/fire.svg b/exam-vue/src/icons/svg/fire.svg new file mode 100644 index 0000000..22bb81d --- /dev/null +++ b/exam-vue/src/icons/svg/fire.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/form.svg b/exam-vue/src/icons/svg/form.svg new file mode 100644 index 0000000..dcbaa18 --- /dev/null +++ b/exam-vue/src/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/fullscreen.svg b/exam-vue/src/icons/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/exam-vue/src/icons/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/guide.svg b/exam-vue/src/icons/svg/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/exam-vue/src/icons/svg/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/hot.svg b/exam-vue/src/icons/svg/hot.svg new file mode 100644 index 0000000..0e0d7fc --- /dev/null +++ b/exam-vue/src/icons/svg/hot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/icon.svg b/exam-vue/src/icons/svg/icon.svg new file mode 100644 index 0000000..82be8ee --- /dev/null +++ b/exam-vue/src/icons/svg/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/international.svg b/exam-vue/src/icons/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/exam-vue/src/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/language.svg b/exam-vue/src/icons/svg/language.svg new file mode 100644 index 0000000..0082b57 --- /dev/null +++ b/exam-vue/src/icons/svg/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/link.svg b/exam-vue/src/icons/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/exam-vue/src/icons/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/list.svg b/exam-vue/src/icons/svg/list.svg new file mode 100644 index 0000000..20259ed --- /dev/null +++ b/exam-vue/src/icons/svg/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/lock.svg b/exam-vue/src/icons/svg/lock.svg new file mode 100644 index 0000000..74fee54 --- /dev/null +++ b/exam-vue/src/icons/svg/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/log.svg b/exam-vue/src/icons/svg/log.svg new file mode 100644 index 0000000..1b98022 --- /dev/null +++ b/exam-vue/src/icons/svg/log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/map.svg b/exam-vue/src/icons/svg/map.svg new file mode 100644 index 0000000..5d1518e --- /dev/null +++ b/exam-vue/src/icons/svg/map.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/message.svg b/exam-vue/src/icons/svg/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/exam-vue/src/icons/svg/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/money.svg b/exam-vue/src/icons/svg/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/exam-vue/src/icons/svg/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/nested.svg b/exam-vue/src/icons/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/exam-vue/src/icons/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/notify.svg b/exam-vue/src/icons/svg/notify.svg new file mode 100644 index 0000000..e9641f8 --- /dev/null +++ b/exam-vue/src/icons/svg/notify.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/paper.svg b/exam-vue/src/icons/svg/paper.svg new file mode 100644 index 0000000..54f728b --- /dev/null +++ b/exam-vue/src/icons/svg/paper.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/exam-vue/src/icons/svg/password.svg b/exam-vue/src/icons/svg/password.svg new file mode 100644 index 0000000..e291d85 --- /dev/null +++ b/exam-vue/src/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/pdf.svg b/exam-vue/src/icons/svg/pdf.svg new file mode 100644 index 0000000..957aa0c --- /dev/null +++ b/exam-vue/src/icons/svg/pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/people.svg b/exam-vue/src/icons/svg/people.svg new file mode 100644 index 0000000..2bd54ae --- /dev/null +++ b/exam-vue/src/icons/svg/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/peoples.svg b/exam-vue/src/icons/svg/peoples.svg new file mode 100644 index 0000000..aab852e --- /dev/null +++ b/exam-vue/src/icons/svg/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/qq.svg b/exam-vue/src/icons/svg/qq.svg new file mode 100644 index 0000000..ee13d4e --- /dev/null +++ b/exam-vue/src/icons/svg/qq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/repo.svg b/exam-vue/src/icons/svg/repo.svg new file mode 100644 index 0000000..0a708d0 --- /dev/null +++ b/exam-vue/src/icons/svg/repo.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/results.svg b/exam-vue/src/icons/svg/results.svg new file mode 100644 index 0000000..937a6e1 --- /dev/null +++ b/exam-vue/src/icons/svg/results.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/review.svg b/exam-vue/src/icons/svg/review.svg new file mode 100644 index 0000000..707e60d --- /dev/null +++ b/exam-vue/src/icons/svg/review.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/role.svg b/exam-vue/src/icons/svg/role.svg new file mode 100644 index 0000000..2a9abb8 --- /dev/null +++ b/exam-vue/src/icons/svg/role.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/search.svg b/exam-vue/src/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/exam-vue/src/icons/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/settings .svg b/exam-vue/src/icons/svg/settings .svg new file mode 100644 index 0000000..e0ef4c5 --- /dev/null +++ b/exam-vue/src/icons/svg/settings .svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/shopping.svg b/exam-vue/src/icons/svg/shopping.svg new file mode 100644 index 0000000..87513e7 --- /dev/null +++ b/exam-vue/src/icons/svg/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/size.svg b/exam-vue/src/icons/svg/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/exam-vue/src/icons/svg/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/skill.svg b/exam-vue/src/icons/svg/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/exam-vue/src/icons/svg/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/star.svg b/exam-vue/src/icons/svg/star.svg new file mode 100644 index 0000000..6cf86e6 --- /dev/null +++ b/exam-vue/src/icons/svg/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/statis.svg b/exam-vue/src/icons/svg/statis.svg new file mode 100644 index 0000000..6adf50c --- /dev/null +++ b/exam-vue/src/icons/svg/statis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/stats-dots.svg b/exam-vue/src/icons/svg/stats-dots.svg new file mode 100644 index 0000000..748e136 --- /dev/null +++ b/exam-vue/src/icons/svg/stats-dots.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/stats2.svg b/exam-vue/src/icons/svg/stats2.svg new file mode 100644 index 0000000..407d569 --- /dev/null +++ b/exam-vue/src/icons/svg/stats2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/study.svg b/exam-vue/src/icons/svg/study.svg new file mode 100644 index 0000000..745fc78 --- /dev/null +++ b/exam-vue/src/icons/svg/study.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/study1.svg b/exam-vue/src/icons/svg/study1.svg new file mode 100644 index 0000000..fcfd935 --- /dev/null +++ b/exam-vue/src/icons/svg/study1.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/support.svg b/exam-vue/src/icons/svg/support.svg new file mode 100644 index 0000000..d06c14f --- /dev/null +++ b/exam-vue/src/icons/svg/support.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/tab.svg b/exam-vue/src/icons/svg/tab.svg new file mode 100644 index 0000000..b4b48e4 --- /dev/null +++ b/exam-vue/src/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/table.svg b/exam-vue/src/icons/svg/table.svg new file mode 100644 index 0000000..0e3dc9d --- /dev/null +++ b/exam-vue/src/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/test.svg b/exam-vue/src/icons/svg/test.svg new file mode 100644 index 0000000..31e22a3 --- /dev/null +++ b/exam-vue/src/icons/svg/test.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/theme.svg b/exam-vue/src/icons/svg/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/exam-vue/src/icons/svg/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/topic.svg b/exam-vue/src/icons/svg/topic.svg new file mode 100644 index 0000000..11152ee --- /dev/null +++ b/exam-vue/src/icons/svg/topic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/training.svg b/exam-vue/src/icons/svg/training.svg new file mode 100644 index 0000000..ec110df --- /dev/null +++ b/exam-vue/src/icons/svg/training.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/tree-table.svg b/exam-vue/src/icons/svg/tree-table.svg new file mode 100644 index 0000000..8aafdb8 --- /dev/null +++ b/exam-vue/src/icons/svg/tree-table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/tree.svg b/exam-vue/src/icons/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/exam-vue/src/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/user.svg b/exam-vue/src/icons/svg/user.svg new file mode 100644 index 0000000..0ba0716 --- /dev/null +++ b/exam-vue/src/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/water.svg b/exam-vue/src/icons/svg/water.svg new file mode 100644 index 0000000..77a50ed --- /dev/null +++ b/exam-vue/src/icons/svg/water.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/wechat.svg b/exam-vue/src/icons/svg/wechat.svg new file mode 100644 index 0000000..c586e55 --- /dev/null +++ b/exam-vue/src/icons/svg/wechat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svg/zip.svg b/exam-vue/src/icons/svg/zip.svg new file mode 100644 index 0000000..f806fc4 --- /dev/null +++ b/exam-vue/src/icons/svg/zip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exam-vue/src/icons/svgo.yml b/exam-vue/src/icons/svgo.yml new file mode 100644 index 0000000..d11906a --- /dev/null +++ b/exam-vue/src/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/exam-vue/src/layout/components/AppMain.vue b/exam-vue/src/layout/components/AppMain.vue new file mode 100644 index 0000000..a897638 --- /dev/null +++ b/exam-vue/src/layout/components/AppMain.vue @@ -0,0 +1,57 @@ + + + + + + + diff --git a/exam-vue/src/layout/components/Navbar.vue b/exam-vue/src/layout/components/Navbar.vue new file mode 100644 index 0000000..6cebf65 --- /dev/null +++ b/exam-vue/src/layout/components/Navbar.vue @@ -0,0 +1,155 @@ + + + + + diff --git a/exam-vue/src/layout/components/Settings/index.vue b/exam-vue/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..32ef018 --- /dev/null +++ b/exam-vue/src/layout/components/Settings/index.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/exam-vue/src/layout/components/Sidebar/FixiOSBug.js b/exam-vue/src/layout/components/Sidebar/FixiOSBug.js new file mode 100644 index 0000000..bc14856 --- /dev/null +++ b/exam-vue/src/layout/components/Sidebar/FixiOSBug.js @@ -0,0 +1,26 @@ +export default { + computed: { + device() { + return this.$store.state.app.device + } + }, + mounted() { + // In order to fix the click on menu on the ios device will trigger the mouseleave bug + // https://github.com/PanJiaChen/vue-element-admin/issues/1135 + this.fixBugIniOS() + }, + methods: { + fixBugIniOS() { + const $subMenu = this.$refs.subMenu + if ($subMenu) { + const handleMouseleave = $subMenu.handleMouseleave + $subMenu.handleMouseleave = (e) => { + if (this.device === 'mobile') { + return + } + handleMouseleave(e) + } + } + } + } +} diff --git a/exam-vue/src/layout/components/Sidebar/Item.vue b/exam-vue/src/layout/components/Sidebar/Item.vue new file mode 100644 index 0000000..b515f61 --- /dev/null +++ b/exam-vue/src/layout/components/Sidebar/Item.vue @@ -0,0 +1,29 @@ + diff --git a/exam-vue/src/layout/components/Sidebar/Link.vue b/exam-vue/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..eb4dd10 --- /dev/null +++ b/exam-vue/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,36 @@ + + + + diff --git a/exam-vue/src/layout/components/Sidebar/Logo.vue b/exam-vue/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..cf7f273 --- /dev/null +++ b/exam-vue/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/exam-vue/src/layout/components/Sidebar/SidebarItem.vue b/exam-vue/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..2d49dd8 --- /dev/null +++ b/exam-vue/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,95 @@ + + + diff --git a/exam-vue/src/layout/components/Sidebar/index.vue b/exam-vue/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..fb014a2 --- /dev/null +++ b/exam-vue/src/layout/components/Sidebar/index.vue @@ -0,0 +1,54 @@ + + + diff --git a/exam-vue/src/layout/components/TagsView/ScrollPane.vue b/exam-vue/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..0d5ac5a --- /dev/null +++ b/exam-vue/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/exam-vue/src/layout/components/TagsView/index.vue b/exam-vue/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..7739468 --- /dev/null +++ b/exam-vue/src/layout/components/TagsView/index.vue @@ -0,0 +1,289 @@ + + + + + + + diff --git a/exam-vue/src/layout/components/index.js b/exam-vue/src/layout/components/index.js new file mode 100644 index 0000000..104bd3a --- /dev/null +++ b/exam-vue/src/layout/components/index.js @@ -0,0 +1,5 @@ +export { default as AppMain } from './AppMain' +export { default as Navbar } from './Navbar' +export { default as Settings } from './Settings' +export { default as Sidebar } from './Sidebar/index.vue' +export { default as TagsView } from './TagsView/index.vue' diff --git a/exam-vue/src/layout/index.vue b/exam-vue/src/layout/index.vue new file mode 100644 index 0000000..965bcd1 --- /dev/null +++ b/exam-vue/src/layout/index.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/exam-vue/src/layout/mixin/ResizeHandler.js b/exam-vue/src/layout/mixin/ResizeHandler.js new file mode 100644 index 0000000..e8d0df8 --- /dev/null +++ b/exam-vue/src/layout/mixin/ResizeHandler.js @@ -0,0 +1,45 @@ +import store from '@/store' + +const { body } = document +const WIDTH = 992 // refer to Bootstrap's responsive design + +export default { + watch: { + $route(route) { + if (this.device === 'mobile' && this.sidebar.opened) { + store.dispatch('app/closeSideBar', { withoutAnimation: false }) + } + } + }, + beforeMount() { + window.addEventListener('resize', this.$_resizeHandler) + }, + beforeDestroy() { + window.removeEventListener('resize', this.$_resizeHandler) + }, + mounted() { + const isMobile = this.$_isMobile() + if (isMobile) { + store.dispatch('app/toggleDevice', 'mobile') + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_isMobile() { + const rect = body.getBoundingClientRect() + return rect.width - 1 < WIDTH + }, + $_resizeHandler() { + if (!document.hidden) { + const isMobile = this.$_isMobile() + store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') + + if (isMobile) { + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + } + } + } +} diff --git a/exam-vue/src/main.js b/exam-vue/src/main.js new file mode 100644 index 0000000..9c2a4b3 --- /dev/null +++ b/exam-vue/src/main.js @@ -0,0 +1,41 @@ +import Vue from 'vue' + +import Cookies from 'js-cookie' + +import 'normalize.css/normalize.css' // a modern alternative to CSS resets + +import Element from 'element-ui' +import './styles/element-variables.scss' +import '@/styles/index.scss' + +import App from './App' +import store from './store' +import router from './router' + +import './icons' // icon +import './permission' // permission control +import './utils/error-log' // error log + +import * as filters from './filters' + +// Element UI +Vue.use(Element, { + size: Cookies.get('size') || 'medium' // set element-ui default size +}) + +// 注册全局过滤器 +Object.keys(filters).forEach(key => { + Vue.filter(key, filters[key]) +}) + +Vue.config.productionTip = false + +// 环境标识 +Vue.prototype.$demo = process.env.NODE_ENV === 'demo' + +new Vue({ + el: '#app', + router, + store, + render: h => h(App) +}) diff --git a/exam-vue/src/permission.js b/exam-vue/src/permission.js new file mode 100644 index 0000000..f26594e --- /dev/null +++ b/exam-vue/src/permission.js @@ -0,0 +1,68 @@ +import router from './router' +import store from './store' +import { Message } from 'element-ui' +import NProgress from 'nprogress' // progress bar +import 'nprogress/nprogress.css' // progress bar style +import { getToken } from '@/utils/auth' // get token from cookie +import getPageTitle from '@/utils/get-page-title' + +NProgress.configure({ showSpinner: false }) // NProgress Configuration + +const whiteList = ['/login', '/register'] // no redirect whitelist + +router.beforeEach(async(to, from, next) => { + // start progress bar + NProgress.start() + + // 获取网站基本信息 + let siteData = store.getters.siteData + if (!siteData.siteName) { + siteData = await store.dispatch('settings/getSite') + } + + // 页面标题 + document.title = getPageTitle(siteData.siteName, to.meta.title) + + // 本地token + const hasToken = getToken() + + if (hasToken) { + if (to.path === '/login') { + next({ path: '/' }) + NProgress.done() + } else { + const hasRoles = store.getters.roles && store.getters.roles.length > 0 + if (hasRoles) { + next() + } else { + try { + // 读取用户权限 + const { roles } = await store.dispatch('user/getInfo') + const accessRoutes = await store.dispatch('permission/generateRoutes', roles) + router.addRoutes(accessRoutes) + next({ ...to, replace: true }) + } catch (error) { + // 出错注销 + await store.dispatch('user/resetToken') + Message.error(error || 'Has Error') + next(`/login?redirect=${to.path}`) + NProgress.done() + } + } + } + } else { + // 排除白名单 + if (whiteList.indexOf(to.path) !== -1) { + next() + } else { + // 跳转到登录页面 + next(`/login?redirect=${to.path}`) + NProgress.done() + } + } +}) + +router.afterEach(() => { + // finish progress bar + NProgress.done() +}) diff --git a/exam-vue/src/router/index.js b/exam-vue/src/router/index.js new file mode 100644 index 0000000..8532762 --- /dev/null +++ b/exam-vue/src/router/index.js @@ -0,0 +1,324 @@ +import Vue from 'vue' +import Router from 'vue-router' + +Vue.use(Router) + +// 主要框架 +import Layout from '@/layout' + +// 登录框架 +import LoginLayout from '@/views/login/components/LoginLayout' + +export const constantRoutes = [ + { + path: '/redirect', + component: Layout, + hidden: true, + children: [ + { + path: '/redirect/:path*', + component: () => import('@/views/redirect/index') + } + ] + }, + + { + path: '/login', + component: LoginLayout, + hidden: true, + children: [ + { + path: '/login', + name: 'Login', + component: () => import('@/views/login/index'), + hidden: true + }, + { + path: '/register', + name: 'Register', + component: () => import('@/views/login/register'), + hidden: true + } + ] + }, + + { + path: '/404', + component: () => import('@/views/error-page/404'), + hidden: true + }, + { + path: '/401', + component: () => import('@/views/error-page/401'), + hidden: true + }, + { + path: '/', + component: Layout, + redirect: '/dashboard', + children: [ + { + path: 'dashboard', + component: () => import('@/views/dashboard/index'), + name: 'Dashboard', + meta: { title: '控制台', icon: 'dashboard', affix: true } + }, + + { + path: 'qu/view/:id', + component: () => import('@/views/qu/qu/view'), + name: 'ViewQu', + meta: { title: '题目详情', noCache: true, activeMenu: '/manage/qu' }, + hidden: true + } + ] + }, + + { + path: '/profile', + component: Layout, + redirect: '/profile/index', + hidden: true, + children: [ + { + path: 'index', + component: () => import('@/views/profile/index'), + name: 'Profile', + meta: { title: '个人资料', icon: 'user', noCache: true } + } + ] + } +] + +/** + * asyncRoutes + * the routes that need to be dynamically loaded based on user roles + */ +export const asyncRoutes = [ + + { + path: '/exam/start/:id', + component: () => import('@/views/paper/exam/exam'), + name: 'StartExam', + meta: { title: '开始考试' }, + hidden: true + }, + + { + path: '/my', + component: Layout, + redirect: '/my/exam', + name: 'Online', + meta: { + title: '在线考试', + icon: 'list', + roles: ['student', 'sa'] + }, + children: [ + + { + path: 'exam', + component: () => import('@/views/paper/exam/list'), + name: 'ExamOnline', + meta: { title: '在线考试', noCache: true, icon: 'guide' } + }, + + { + path: 'exam/prepare/:examId', + component: () => import('@/views/paper/exam/preview'), + name: 'PreExam', + meta: { title: '准备考试', noCache: true, activeMenu: '/my/exam' }, + hidden: true + }, + + { + path: 'exam/result/:id', + component: () => import('@/views/paper/exam/result'), + name: 'ShowExam', + meta: { title: '考试结果', noCache: true, activeMenu: '/online/exam' }, + hidden: true + }, + + { + path: 'exam/records', + component: () => import('@/views/user/exam/my'), + name: 'ListMyExam', + meta: { title: '我的成绩', noCache: true, icon: 'results' } + }, + + { + path: 'book/list/:examId', + component: () => import('@/views/user/book'), + name: 'BookList', + meta: { title: '考试错题', noCache: true, activeMenu: '/my/exam/records' }, + hidden: true + }, + + { + path: 'book/training/:examId', + component: () => import('@/views/user/book/train'), + name: 'BookTraining', + meta: { title: '错题训练', noCache: true, activeMenu: '/my/exam/records' }, + hidden: true + } + + ] + }, + + { + path: '/exam', + component: Layout, + redirect: '/exam/repo', + name: 'Manage', + meta: { + title: '考试管理', + icon: 'example', + roles: ['sa', 'teacher'] + }, + children: [ + + { + path: 'repo', + component: () => import('@/views/qu/repo'), + name: 'ListRepo', + meta: { title: '题库管理', noCache: true, icon: 'repo' } + }, + + { + path: 'repo/add', + component: () => import('@/views/qu/repo/form'), + name: 'AddRepo', + meta: { title: '添加题库', noCache: true, activeMenu: '/exam/repo' }, + hidden: true + }, + + { + path: 'repo/update/:id', + component: () => import('@/views/qu/repo/form'), + name: 'UpdateRepo', + meta: { title: '题库详情', noCache: true, activeMenu: '/exam/repo' }, + hidden: true + }, + + { + path: 'qu', + component: () => import('@/views/qu/qu'), + name: 'ListQu', + meta: { title: '试题管理', noCache: true, icon: 'support' } + }, + + { + path: 'qu/add', + component: () => import('@/views/qu/qu/form'), + name: 'AddQu', + meta: { title: '添加试题', noCache: true, activeMenu: '/exam/qu' }, + hidden: true + }, + + { + path: 'qu/update/:id', + component: () => import('@/views/qu/qu/form'), + name: 'UpdateQu', + meta: { title: '修改试题', noCache: true, activeMenu: '/exam/qu' }, + hidden: true + }, + + { + path: 'exam', + component: () => import('@/views/exam/exam'), + name: 'ListExam', + meta: { title: '考试管理', noCache: true, icon: 'log' } + }, + + { + path: 'exam/add', + component: () => import('@/views/exam/exam/form'), + name: 'AddExam', + meta: { title: '添加考试', noCache: true, activeMenu: '/exam/exam' }, + hidden: true + }, + + { + path: 'exam/update/:id', + component: () => import('@/views/exam/exam/form'), + name: 'UpdateExam', + meta: { title: '修改考试', noCache: true, activeMenu: '/exam/exam' }, + hidden: true + }, + { + path: 'exam/users/:examId', + component: () => import('@/views/user/exam'), + name: 'ListExamUser', + meta: { title: '考试人员', noCache: true, activeMenu: '/exam/exam' }, + hidden: true + }, + { + path: 'exam/paper/:examId', + component: () => import('@/views/paper/paper'), + name: 'ListPaper', + meta: { title: '考试记录', noCache: true, activeMenu: '/exam/exam' }, + hidden: true + } + ] + }, + + { + path: '/sys', + component: Layout, + redirect: '/sys/config', + name: 'Sys', + meta: { + title: '系统设置', + icon: 'configure', + roles: ['sa'] + }, + children: [ + { + path: 'config', + component: () => import('@/views/sys/config'), + name: 'SysConfig', + meta: { title: '系统配置', icon: 'theme' } + }, + + { + path: 'depart', + component: () => import('@/views/sys/depart'), + name: 'SysDepart', + meta: { title: '部门管理', icon: 'tree' } + }, + + { + path: 'role', + component: () => import('@/views/sys/role'), + name: 'SysRole', + meta: { title: '角色管理', icon: 'role' } + }, + + { + path: 'user', + component: () => import('@/views/sys/user'), + name: 'SysUser', + meta: { title: '用户管理', icon: 'admin' } + } + + ] + }, + + // 404 page must be placed at the end !!! + { path: '*', redirect: '/dashboard', hidden: true } +] + +const createRouter = () => new Router({ + // mode: 'history', // require service support + scrollBehavior: () => ({ y: 0 }), + routes: constantRoutes +}) + +const router = createRouter() + +export function resetRouter() { + const newRouter = createRouter() + router.matcher = newRouter.matcher // reset router +} + +export default router diff --git a/exam-vue/src/settings.js b/exam-vue/src/settings.js new file mode 100644 index 0000000..0ddcf99 --- /dev/null +++ b/exam-vue/src/settings.js @@ -0,0 +1,35 @@ +module.exports = { + title: '云帆考试培训系统', + + /** + * @type {boolean} true | false + * @description Whether show the settings right-panel + */ + showSettings: false, + + /** + * @type {boolean} true | false + * @description Whether need tagsView + */ + tagsView: true, + + /** + * @type {boolean} true | false + * @description Whether fix the header + */ + fixedHeader: false, + + /** + * @type {boolean} true | false + * @description Whether show the logo in sidebar + */ + sidebarLogo: true, + + /** + * @type {string | array} 'production' | ['production', 'development'] + * @description Need show err logs component. + * The default is only used in the production env + * If you want to also use it in dev, you can pass ['production', 'development'] + */ + errorLog: 'production' +} diff --git a/exam-vue/src/store/getters.js b/exam-vue/src/store/getters.js new file mode 100644 index 0000000..11f131e --- /dev/null +++ b/exam-vue/src/store/getters.js @@ -0,0 +1,18 @@ +const getters = { + sidebar: state => state.app.sidebar, + size: state => state.app.size, + device: state => state.app.device, + visitedViews: state => state.tagsView.visitedViews, + cachedViews: state => state.tagsView.cachedViews, + token: state => state.user.token, + avatar: state => state.user.avatar, + userId: state => state.user.userId, + name: state => state.user.name, + realName: state => state.user.realName, + introduction: state => state.user.introduction, + roles: state => state.user.roles, + permission_routes: state => state.permission.routes, + errorLogs: state => state.errorLog.logs, + siteData: state => state.settings.siteData +} +export default getters diff --git a/exam-vue/src/store/index.js b/exam-vue/src/store/index.js new file mode 100644 index 0000000..0fd8395 --- /dev/null +++ b/exam-vue/src/store/index.js @@ -0,0 +1,25 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import getters from './getters' + +Vue.use(Vuex) + +// https://webpack.js.org/guides/dependency-management/#requirecontext +const modulesFiles = require.context('./modules', true, /\.js$/) + +// you do not need `import app from './modules/app'` +// it will auto require all vuex module from modules file +const modules = modulesFiles.keys().reduce((modules, modulePath) => { + // set './app.js' => 'app' + const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1') + const value = modulesFiles(modulePath) + modules[moduleName] = value.default + return modules +}, {}) + +const store = new Vuex.Store({ + modules, + getters +}) + +export default store diff --git a/exam-vue/src/store/modules/app.js b/exam-vue/src/store/modules/app.js new file mode 100644 index 0000000..45d89bb --- /dev/null +++ b/exam-vue/src/store/modules/app.js @@ -0,0 +1,56 @@ +import Cookies from 'js-cookie' + +const state = { + sidebar: { + opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, + withoutAnimation: false + }, + device: 'desktop', + size: Cookies.get('size') || 'medium' +} + +const mutations = { + TOGGLE_SIDEBAR: state => { + state.sidebar.opened = !state.sidebar.opened + state.sidebar.withoutAnimation = false + if (state.sidebar.opened) { + Cookies.set('sidebarStatus', 1) + } else { + Cookies.set('sidebarStatus', 0) + } + }, + CLOSE_SIDEBAR: (state, withoutAnimation) => { + Cookies.set('sidebarStatus', 0) + state.sidebar.opened = false + state.sidebar.withoutAnimation = withoutAnimation + }, + TOGGLE_DEVICE: (state, device) => { + state.device = device + }, + SET_SIZE: (state, size) => { + state.size = size + Cookies.set('size', size) + } +} + +const actions = { + toggleSideBar({ commit }) { + commit('TOGGLE_SIDEBAR') + }, + closeSideBar({ commit }, { withoutAnimation }) { + commit('CLOSE_SIDEBAR', withoutAnimation) + }, + toggleDevice({ commit }, device) { + commit('TOGGLE_DEVICE', device) + }, + setSize({ commit }, size) { + commit('SET_SIZE', size) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/exam-vue/src/store/modules/errorLog.js b/exam-vue/src/store/modules/errorLog.js new file mode 100644 index 0000000..6b01f95 --- /dev/null +++ b/exam-vue/src/store/modules/errorLog.js @@ -0,0 +1,28 @@ +const state = { + logs: [] +} + +const mutations = { + ADD_ERROR_LOG: (state, log) => { + state.logs.push(log) + }, + CLEAR_ERROR_LOG: (state) => { + state.logs.splice(0) + } +} + +const actions = { + addErrorLog({ commit }, log) { + commit('ADD_ERROR_LOG', log) + }, + clearErrorLog({ commit }) { + commit('CLEAR_ERROR_LOG') + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/exam-vue/src/store/modules/permission.js b/exam-vue/src/store/modules/permission.js new file mode 100644 index 0000000..65e00d4 --- /dev/null +++ b/exam-vue/src/store/modules/permission.js @@ -0,0 +1,64 @@ +import { asyncRoutes, constantRoutes } from '@/router' + +/** + * Use meta.role to determine if the current user has permission + * @param roles + * @param route + */ +function hasPermission(roles, route) { + if (route.meta && route.meta.roles) { + return roles.some(role => route.meta.roles.includes(role)) + } else { + return true + } +} + +/** + * Filter asynchronous routing tables by recursion + * @param routes asyncRoutes + * @param roles + */ +export function filterAsyncRoutes(routes, roles) { + const res = [] + + routes.forEach(route => { + const tmp = { ...route } + if (hasPermission(roles, tmp)) { + if (tmp.children) { + tmp.children = filterAsyncRoutes(tmp.children, roles) + } + res.push(tmp) + } + }) + + return res +} + +const state = { + routes: [], + addRoutes: [] +} + +const mutations = { + SET_ROUTES: (state, routes) => { + state.addRoutes = routes + state.routes = constantRoutes.concat(routes) + } +} + +const actions = { + generateRoutes({ commit }, roles) { + return new Promise(resolve => { + const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles) + commit('SET_ROUTES', accessedRoutes) + resolve(accessedRoutes) + }) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/exam-vue/src/store/modules/settings.js b/exam-vue/src/store/modules/settings.js new file mode 100644 index 0000000..fe688a5 --- /dev/null +++ b/exam-vue/src/store/modules/settings.js @@ -0,0 +1,52 @@ +import variables from '@/styles/element-variables.scss' +import defaultSettings from '@/settings' +import { fetchDetail } from '@/api/sys/config/config' + +const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings + +const state = { + theme: variables.theme, + showSettings: showSettings, + tagsView: tagsView, + fixedHeader: fixedHeader, + sidebarLogo: sidebarLogo, + siteData: {} +} + +const mutations = { + CHANGE_SETTING: (state, { key, value }) => { + if (state.hasOwnProperty(key)) { + state[key] = value + } + }, + SET_SITE_DATA: (state, siteData) => { + state.siteData = siteData + } + +} + +const actions = { + changeSetting({ commit }, data) { + commit('CHANGE_SETTING', data) + }, + // 获取网站配置信息 + getSite({ commit }) { + return new Promise((resolve, reject) => { + fetchDetail({}).then(response => { + const { data } = response + commit('SET_SITE_DATA', data) + resolve(data) + }).catch(error => { + reject(error) + }) + }) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} + diff --git a/exam-vue/src/store/modules/tagsView.js b/exam-vue/src/store/modules/tagsView.js new file mode 100644 index 0000000..57e7242 --- /dev/null +++ b/exam-vue/src/store/modules/tagsView.js @@ -0,0 +1,160 @@ +const state = { + visitedViews: [], + cachedViews: [] +} + +const mutations = { + ADD_VISITED_VIEW: (state, view) => { + if (state.visitedViews.some(v => v.path === view.path)) return + state.visitedViews.push( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) + }, + ADD_CACHED_VIEW: (state, view) => { + if (state.cachedViews.includes(view.name)) return + if (!view.meta.noCache) { + state.cachedViews.push(view.name) + } + }, + + DEL_VISITED_VIEW: (state, view) => { + for (const [i, v] of state.visitedViews.entries()) { + if (v.path === view.path) { + state.visitedViews.splice(i, 1) + break + } + } + }, + DEL_CACHED_VIEW: (state, view) => { + const index = state.cachedViews.indexOf(view.name) + index > -1 && state.cachedViews.splice(index, 1) + }, + + DEL_OTHERS_VISITED_VIEWS: (state, view) => { + state.visitedViews = state.visitedViews.filter(v => { + return v.meta.affix || v.path === view.path + }) + }, + DEL_OTHERS_CACHED_VIEWS: (state, view) => { + const index = state.cachedViews.indexOf(view.name) + if (index > -1) { + state.cachedViews = state.cachedViews.slice(index, index + 1) + } else { + // if index = -1, there is no cached tags + state.cachedViews = [] + } + }, + + DEL_ALL_VISITED_VIEWS: state => { + // keep affix tags + const affixTags = state.visitedViews.filter(tag => tag.meta.affix) + state.visitedViews = affixTags + }, + DEL_ALL_CACHED_VIEWS: state => { + state.cachedViews = [] + }, + + UPDATE_VISITED_VIEW: (state, view) => { + for (let v of state.visitedViews) { + if (v.path === view.path) { + v = Object.assign(v, view) + break + } + } + } +} + +const actions = { + addView({ dispatch }, view) { + dispatch('addVisitedView', view) + dispatch('addCachedView', view) + }, + addVisitedView({ commit }, view) { + commit('ADD_VISITED_VIEW', view) + }, + addCachedView({ commit }, view) { + commit('ADD_CACHED_VIEW', view) + }, + + delView({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delVisitedView', view) + dispatch('delCachedView', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delVisitedView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_VISITED_VIEW', view) + resolve([...state.visitedViews]) + }) + }, + delCachedView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_CACHED_VIEW', view) + resolve([...state.cachedViews]) + }) + }, + + delOthersViews({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delOthersVisitedViews', view) + dispatch('delOthersCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delOthersVisitedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_VISITED_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, + delOthersCachedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_CACHED_VIEWS', view) + resolve([...state.cachedViews]) + }) + }, + + delAllViews({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delAllVisitedViews', view) + dispatch('delAllCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delAllVisitedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_VISITED_VIEWS') + resolve([...state.visitedViews]) + }) + }, + delAllCachedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_CACHED_VIEWS') + resolve([...state.cachedViews]) + }) + }, + + updateVisitedView({ commit }, view) { + commit('UPDATE_VISITED_VIEW', view) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/exam-vue/src/store/modules/user.js b/exam-vue/src/store/modules/user.js new file mode 100644 index 0000000..6cf0fd3 --- /dev/null +++ b/exam-vue/src/store/modules/user.js @@ -0,0 +1,160 @@ +import { login, reg, logout, getInfo } from '@/api/user' +import { getToken, setToken, removeToken } from '@/utils/auth' +import router, { resetRouter } from '@/router' + +const state = { + token: getToken(), + userId: '', + name: '', + realName: '', + avatar: '', + introduction: '', + roles: [] +} + +const mutations = { + SET_TOKEN: (state, token) => { + state.token = token + }, + SET_INTRODUCTION: (state, introduction) => { + state.introduction = introduction + }, + SET_ID: (state, userId) => { + state.userId = userId + }, + SET_NAME: (state, name) => { + state.name = name + }, + SET_REAL_NAME: (state, realName) => { + state.realName = realName + }, + SET_AVATAR: (state, avatar) => { + state.avatar = avatar + }, + SET_ROLES: (state, roles) => { + state.roles = roles + } +} + +const actions = { + // user login + login({ commit }, userInfo) { + const { username, password } = userInfo + return new Promise((resolve, reject) => { + login({ username: username.trim(), password: password }).then(response => { + const { data } = response + commit('SET_TOKEN', data.token) + setToken(data.token) + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + reg({ commit }, userInfo) { + const { userName, realName, password } = userInfo + return new Promise((resolve, reject) => { + reg({ userName: userName.trim(), realName: realName.trim(), password: password }).then(response => { + const { data } = response + commit('SET_TOKEN', data.token) + setToken(data.token) + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // get user info + getInfo({ commit, state }) { + return new Promise((resolve, reject) => { + getInfo(state.token).then(response => { + const { data } = response + + if (!data) { + reject('校验失败,请重新登录!.') + } + + const { id, roles, userName, realName, avatar, introduction } = data + + // roles must be a non-empty array + if (!roles || roles.length <= 0) { + reject('用户角色不能为空!') + } + + commit('SET_ID', id) + commit('SET_ROLES', roles) + commit('SET_REAL_NAME', realName) + commit('SET_NAME', userName) + commit('SET_AVATAR', avatar) + commit('SET_INTRODUCTION', introduction) + resolve(data) + }).catch(error => { + reject(error) + }) + }) + }, + + // user logout + logout({ commit, state, dispatch }) { + return new Promise((resolve, reject) => { + logout(state.token).then(() => { + commit('SET_TOKEN', '') + commit('SET_ROLES', []) + removeToken() + resetRouter() + + // reset visited views and cached views + // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485 + dispatch('tagsView/delAllViews', null, { root: true }) + + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // remove token + resetToken({ commit }) { + return new Promise(resolve => { + commit('SET_TOKEN', '') + commit('SET_ROLES', []) + removeToken() + resolve() + }) + }, + + // dynamically modify permissions + changeRoles({ commit, dispatch }, role) { + return new Promise(async resolve => { + const token = role + '-token' + + commit('SET_TOKEN', token) + setToken(token) + + const { roles } = await dispatch('getInfo') + + resetRouter() + + // generate accessible routes map based on roles + const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true }) + + // dynamically add accessible routes + router.addRoutes(accessRoutes) + + // reset visited views and cached views + dispatch('tagsView/delAllViews', null, { root: true }) + + resolve() + }) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/exam-vue/src/styles/btn.scss b/exam-vue/src/styles/btn.scss new file mode 100644 index 0000000..e6ba1a8 --- /dev/null +++ b/exam-vue/src/styles/btn.scss @@ -0,0 +1,99 @@ +@import './variables.scss'; + +@mixin colorBtn($color) { + background: $color; + + &:hover { + color: $color; + + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue) +} + +.light-blue-btn { + @include colorBtn($light-blue) +} + +.red-btn { + @include colorBtn($red) +} + +.pink-btn { + @include colorBtn($pink) +} + +.green-btn { + @include colorBtn($green) +} + +.tiffany-btn { + @include colorBtn($tiffany) +} + +.yellow-btn { + @include colorBtn($yellow) +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + transition: 600ms ease all; + position: relative; + display: inline-block; + + &:hover { + background: #fff; + + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} diff --git a/exam-vue/src/styles/element-ui.scss b/exam-vue/src/styles/element-ui.scss new file mode 100644 index 0000000..955d3ca --- /dev/null +++ b/exam-vue/src/styles/element-ui.scss @@ -0,0 +1,84 @@ +// cover some element-ui styles + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +.cell { + .el-tag { + margin-right: 0px; + } +} + +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0px; + } + } +} + +// to fixed https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block + } +} + +// fix date-picker ui bug in filter-item +.el-range-editor.el-input__inner { + display: inline-flex !important; +} + +// to fix el-date-picker css style +.el-range-separator { + box-sizing: content-box; +} diff --git a/exam-vue/src/styles/element-variables.scss b/exam-vue/src/styles/element-variables.scss new file mode 100644 index 0000000..30a0e6b --- /dev/null +++ b/exam-vue/src/styles/element-variables.scss @@ -0,0 +1,31 @@ +/** +* I think element-ui's default theme color is too light for long-term use. +* So I modified the default color and you can modify it to your liking. +**/ + +/* theme color */ +$--color-primary: #1890ff; +$--color-success: #13ce66; +$--color-warning: #FFBA00; +$--color-danger: #ff4949; +// $--color-info: #1E1E1E; + +$--button-font-weight: 400; + +// $--color-text-regular: #1f2d3d; + +$--border-color-light: #dfe4ed; +$--border-color-lighter: #e6ebf5; + +$--table-border:1px solid#dfe6ec; + +/* icon font path, required */ +$--font-path: '~element-ui/lib/theme-chalk/fonts'; + +@import "~element-ui/packages/theme-chalk/src/index"; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + theme: $--color-primary; +} diff --git a/exam-vue/src/styles/index.scss b/exam-vue/src/styles/index.scss new file mode 100644 index 0000000..f67857c --- /dev/null +++ b/exam-vue/src/styles/index.scss @@ -0,0 +1,212 @@ +@import './variables.scss'; +@import './mixin.scss'; +@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; +@import './btn.scss'; + +body { + height: 100%; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +.no-padding { + padding: 0px !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +aside { + background: #eef1f6; + padding: 8px 24px; + margin-bottom: 20px; + border-radius: 2px; + display: block; + line-height: 32px; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + color: #2c3e50; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + a { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } + } +} + +//main-container全局样式 +.app-container { + padding: 20px; +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.pagination-container { + margin-top: 30px; +} + +.text-center { + text-align: center +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + + .subtitle { + font-size: 20px; + color: #fff; + } + + &.draft { + background: #d0d0d0; + } + + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } +} + +.filter-container { + padding-bottom: 10px; + + .filter-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + } +} + +//refine vue-multiselect plugin +.multiselect { + line-height: 16px; +} + +.multiselect--active { + z-index: 1000 !important; +} + + + +// 标题栏样式 +.title-box{ + width: 100%; + border-bottom: #dfe4ed 2px solid; + display: flex; + margin-bottom: 30px; +} + +.title-box div{ + color: #1890ff; + font-size: 14px; + font-weight: 500; + box-sizing: border-box; + line-height: 40px; + height: 40px; + border-bottom: #1890ff 2px solid; + margin-bottom: -2px; +} diff --git a/exam-vue/src/styles/login.scss b/exam-vue/src/styles/login.scss new file mode 100644 index 0000000..158acd0 --- /dev/null +++ b/exam-vue/src/styles/login.scss @@ -0,0 +1,87 @@ +.login-box { + width: 400px; + background: #fff; + padding: 20px; + border: #eee 1px solid; + border-radius: 5px; + height: 450px; +} + +.el-tab-pane{ + margin-top: 15px; +} + +.login-container{ + display: flex; + flex-direction: column; + background-image: url('~@/assets/login-bg.svg'); + background-repeat: no-repeat; + background-position: center 110px; + background-size: 100%; + height: 100vh; + background-color: #f0f2f5; + overflow: hidden; + align-items: center; + justify-content: center; +} + +.logo-box{ + text-align: center; + line-height: 50px; + margin-bottom: 30px; +} + +.site-title{ + position: relative; + top: 2px; + color: rgba(0, 0, 0, 0.85); + font-weight: 600; + font-size: 33px; + font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif; + line-height: 44px; +} + +.logo-title{ + line-height: 44px; + display: flex; + flex-direction: row; + justify-content: center +} + +.logo-img{ + height: 44px; margin-right: 10px +} + +.footer{ + position: fixed; + bottom: 0px; + width: 100%; + text-align: center; + color: #999; + background: #fcfcfc; + line-height: 48px; +} + +.other{ + color: #606266; + font-size: 26px; + cursor: pointer; + margin-right: 5px; + margin-left: 5px; +} +.wechat :hover{ + color: #03E06E; +} + +.wechat2 :hover{ + color: #0082EF; +} + +.ding :hover{ + color: #3296FA; +} + +::v-deep +.el-form-item__content{ + line-height: 0px !important; +} diff --git a/exam-vue/src/styles/mixin.scss b/exam-vue/src/styles/mixin.scss new file mode 100644 index 0000000..06fa061 --- /dev/null +++ b/exam-vue/src/styles/mixin.scss @@ -0,0 +1,66 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } + + @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/exam-vue/src/styles/sidebar.scss b/exam-vue/src/styles/sidebar.scss new file mode 100644 index 0000000..3dad4c3 --- /dev/null +++ b/exam-vue/src/styles/sidebar.scss @@ -0,0 +1,209 @@ +#app { + + .main-container { + min-height: 100%; + transition: margin-left .28s; + margin-left: $sideBarWidth; + position: relative; + } + + .sidebar-container { + transition: width 0.28s; + width: $sideBarWidth !important; + background-color: $menuBg; + height: 100%; + position: fixed; + font-size: 0px; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0px; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 16px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + // menu hover + .submenu-title-noDropdown, + .el-submenu__title { + &:hover { + background-color: $menuHover !important; + } + } + + .is-active>.el-submenu__title { + color: $subMenuActiveText !important; + } + + & .nest-menu .el-submenu>.el-submenu__title, + & .el-submenu .el-menu-item { + min-width: $sideBarWidth !important; + background-color: $subMenuBg !important; + + &:hover { + background-color: $subMenuHover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-submenu { + overflow: hidden; + + &>.el-submenu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + .el-submenu__icon-arrow { + display: none; + } + } + } + + .el-menu--collapse { + .el-submenu { + &>.el-submenu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-submenu { + min-width: $sideBarWidth !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform .28s; + width: $sideBarWidth !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$sideBarWidth, 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + } + + .nest-menu .el-submenu>.el-submenu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: $menuHover !important; + } + } + + // the scroll bar appears when the subMenu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/exam-vue/src/styles/transition.scss b/exam-vue/src/styles/transition.scss new file mode 100644 index 0000000..4cb27cc --- /dev/null +++ b/exam-vue/src/styles/transition.scss @@ -0,0 +1,48 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/exam-vue/src/styles/variables.scss b/exam-vue/src/styles/variables.scss new file mode 100644 index 0000000..a19c27c --- /dev/null +++ b/exam-vue/src/styles/variables.scss @@ -0,0 +1,35 @@ +// base color +$blue:#324157; +$light-blue:#3A71A8; +$red:#C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow:#FEC171; +$panGreen: #30B08F; + +// sidebar +$menuText:#bfcbd9; +$menuActiveText:#409EFF; +$subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951 + +$menuBg:#304156; +$menuHover:#263445; + +$subMenuBg:#1f2d3d; +$subMenuHover:#001528; + +$sideBarWidth: 210px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuText: $menuText; + menuActiveText: $menuActiveText; + subMenuActiveText: $subMenuActiveText; + menuBg: $menuBg; + menuHover: $menuHover; + subMenuBg: $subMenuBg; + subMenuHover: $subMenuHover; + sideBarWidth: $sideBarWidth; +} diff --git a/exam-vue/src/utils/auth.js b/exam-vue/src/utils/auth.js new file mode 100644 index 0000000..08a43d6 --- /dev/null +++ b/exam-vue/src/utils/auth.js @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie' + +const TokenKey = 'Admin-Token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} diff --git a/exam-vue/src/utils/clipboard.js b/exam-vue/src/utils/clipboard.js new file mode 100644 index 0000000..cf5b07a --- /dev/null +++ b/exam-vue/src/utils/clipboard.js @@ -0,0 +1,32 @@ +import Vue from 'vue' +import Clipboard from 'clipboard' + +function clipboardSuccess() { + Vue.prototype.$message({ + message: 'Copy successfully', + type: 'success', + duration: 1500 + }) +} + +function clipboardError() { + Vue.prototype.$message({ + message: 'Copy failed', + type: 'error' + }) +} + +export default function handleClipboard(text, event) { + const clipboard = new Clipboard(event.target, { + text: () => text + }) + clipboard.on('success', () => { + clipboardSuccess() + clipboard.destroy() + }) + clipboard.on('error', () => { + clipboardError() + clipboard.destroy() + }) + clipboard.onClick(event) +} diff --git a/exam-vue/src/utils/error-log.js b/exam-vue/src/utils/error-log.js new file mode 100644 index 0000000..a7f5b55 --- /dev/null +++ b/exam-vue/src/utils/error-log.js @@ -0,0 +1,35 @@ +import Vue from 'vue' +import store from '@/store' +import { isString, isArray } from '@/utils/validate' +import settings from '@/settings' + +// you can set in settings.js +// errorLog:'production' | ['production', 'development'] +const { errorLog: needErrorLog } = settings + +function checkNeed() { + const env = process.env.NODE_ENV + if (isString(needErrorLog)) { + return env === needErrorLog + } + if (isArray(needErrorLog)) { + return needErrorLog.includes(env) + } + return false +} + +if (checkNeed()) { + Vue.config.errorHandler = function(err, vm, info, a) { + // Don't ask me why I use Vue.nextTick, it just a hack. + // detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500 + Vue.nextTick(() => { + store.dispatch('errorLog/addErrorLog', { + err, + vm, + info, + url: window.location.href + }) + console.error(err, info) + }) + } +} diff --git a/exam-vue/src/utils/get-page-title.js b/exam-vue/src/utils/get-page-title.js new file mode 100644 index 0000000..0a34493 --- /dev/null +++ b/exam-vue/src/utils/get-page-title.js @@ -0,0 +1,6 @@ +export default function getPageTitle(title, pageTitle) { + if (pageTitle) { + return `${pageTitle} - ${title}` + } + return `${title}` +} diff --git a/exam-vue/src/utils/index.js b/exam-vue/src/utils/index.js new file mode 100644 index 0000000..2684e3c --- /dev/null +++ b/exam-vue/src/utils/index.js @@ -0,0 +1,347 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * Parse the time to string + * @param {(Object|string|number)} time + * @param {string} cFormat + * @returns {string | null} + */ +export function parseTime(time, cFormat) { + if (arguments.length === 0) { + return null + } + const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time + } else { + if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { + time = parseInt(time) + } + if ((typeof time === 'number') && (time.toString().length === 10)) { + time = time * 1000 + } + date = new Date(time) + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + } + const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => { + const value = formatObj[key] + // Note: getDay() returns 0 on Sunday + if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] } + return value.toString().padStart(2, '0') + }) + return time_str +} + +/** + * @param {number} time + * @param {string} option + * @returns {string} + */ +export function formatTime(time, option) { + if (('' + time).length === 10) { + time = parseInt(time) * 1000 + } else { + time = +time + } + const d = new Date(time) + const now = Date.now() + + const diff = (now - d) / 1000 + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) + } else { + return ( + d.getMonth() + + 1 + + '月' + + d.getDate() + + '日' + + d.getHours() + + '时' + + d.getMinutes() + + '分' + ) + } +} + +/** + * @param {string} url + * @returns {Object} + */ +export function getQueryObject(url) { + url = url == null ? window.location.href : url + const search = url.substring(url.lastIndexOf('?') + 1) + const obj = {} + const reg = /([^?&=]+)=([^?&=]*)/g + search.replace(reg, (rs, $1, $2) => { + const name = decodeURIComponent($1) + let val = decodeURIComponent($2) + val = String(val) + obj[name] = val + return rs + }) + return obj +} + +/** + * @param {string} input value + * @returns {number} output value + */ +export function byteLength(str) { + // returns the byte length of an utf8 string + let s = str.length + for (var i = str.length - 1; i >= 0; i--) { + const code = str.charCodeAt(i) + if (code > 0x7f && code <= 0x7ff) s++ + else if (code > 0x7ff && code <= 0xffff) s += 2 + if (code >= 0xDC00 && code <= 0xDFFF) i-- + } + return s +} + +/** + * @param {Array} actual + * @returns {Array} + */ +export function cleanArray(actual) { + const newArray = [] + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]) + } + } + return newArray +} + +/** + * @param {Object} json + * @returns {Array} + */ +export function param(json) { + if (!json) return '' + return cleanArray( + Object.keys(json).map(key => { + if (json[key] === undefined) return '' + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) + }) + ).join('&') +} + +/** + * @param {string} url + * @returns {Object} + */ +export function param2Obj(url) { + const search = url.split('?')[1] + if (!search) { + return {} + } + return JSON.parse( + '{"' + + decodeURIComponent(search) + .replace(/"/g, '\\"') + .replace(/&/g, '","') + .replace(/=/g, '":"') + .replace(/\+/g, ' ') + + '"}' + ) +} + +/** + * @param {string} val + * @returns {string} + */ +export function html2Text(val) { + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText +} + +/** + * Merges two objects, giving the last one precedence + * @param {Object} target + * @param {(Object|Array)} source + * @returns {Object} + */ +export function objectMerge(target, source) { + if (typeof target !== 'object') { + target = {} + } + if (Array.isArray(source)) { + return source.slice() + } + Object.keys(source).forEach(property => { + const sourceProperty = source[property] + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty) + } else { + target[property] = sourceProperty + } + }) + return target +} + +/** + * @param {HTMLElement} element + * @param {string} className + */ +export function toggleClass(element, className) { + if (!element || !className) { + return + } + let classString = element.className + const nameIndex = classString.indexOf(className) + if (nameIndex === -1) { + classString += '' + className + } else { + classString = + classString.substr(0, nameIndex) + + classString.substr(nameIndex + className.length) + } + element.className = classString +} + +/** + * @param {string} type + * @returns {Date} + */ +export function getTime(type) { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90 + } else { + return new Date(new Date().toDateString()) + } +} + +/** + * @param {Function} func + * @param {number} wait + * @param {boolean} immediate + * @return {*} + */ +export function debounce(func, wait, immediate) { + let timeout, args, context, timestamp, result + + const later = function() { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp + + // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last) + } else { + timeout = null + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args) + if (!timeout) context = args = null + } + } + } + + return function(...args) { + context = this + timestamp = +new Date() + const callNow = immediate && !timeout + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait) + if (callNow) { + result = func.apply(context, args) + context = args = null + } + + return result + } +} + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param {Object} source + * @returns {Object} + */ +export function deepClone(source) { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'deepClone') + } + const targetObj = source.constructor === Array ? [] : {} + Object.keys(source).forEach(keys => { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = deepClone(source[keys]) + } else { + targetObj[keys] = source[keys] + } + }) + return targetObj +} + +/** + * @param {Array} arr + * @returns {Array} + */ +export function uniqueArr(arr) { + return Array.from(new Set(arr)) +} + +/** + * @returns {string} + */ +export function createUniqueString() { + const timestamp = +new Date() + '' + const randomNum = parseInt((1 + Math.random()) * 65536) + '' + return (+(randomNum + timestamp)).toString(32) +} + +/** + * Check if an element has a class + * @param {HTMLElement} elm + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele, cls) { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) +} + +/** + * Add class to element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function addClass(ele, cls) { + if (!hasClass(ele, cls)) ele.className += ' ' + cls +} + +/** + * Remove class from element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function removeClass(ele, cls) { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') + ele.className = ele.className.replace(reg, ' ') + } +} diff --git a/exam-vue/src/utils/open-window.js b/exam-vue/src/utils/open-window.js new file mode 100644 index 0000000..1a655d7 --- /dev/null +++ b/exam-vue/src/utils/open-window.js @@ -0,0 +1,25 @@ +/** + *Created by PanJiaChen on 16/11/29. + * @param {Sting} url + * @param {Sting} title + * @param {Number} w + * @param {Number} h + */ +export default function openWindow(url, title, w, h) { + // Fixes dual-screen position Most browsers Firefox + const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left + const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top + + const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width + const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height + + const left = ((width / 2) - (w / 2)) + dualScreenLeft + const top = ((height / 2) - (h / 2)) + dualScreenTop + const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left) + + // Puts focus on the newWindow + if (window.focus) { + newWindow.focus() + } +} + diff --git a/exam-vue/src/utils/permission.js b/exam-vue/src/utils/permission.js new file mode 100644 index 0000000..221d384 --- /dev/null +++ b/exam-vue/src/utils/permission.js @@ -0,0 +1,25 @@ +import store from '@/store' + +/** + * @param {Array} value + * @returns {Boolean} + * @example see @/views/permission/directive.vue + */ +export default function checkPermission(value) { + if (value && value instanceof Array && value.length > 0) { + const roles = store.getters && store.getters.roles + const permissionRoles = value + + const hasPermission = roles.some(role => { + return permissionRoles.includes(role) + }) + + if (!hasPermission) { + return false + } + return true + } else { + console.error(`need roles! Like v-permission="['admin','editor']"`) + return false + } +} diff --git a/exam-vue/src/utils/request.js b/exam-vue/src/utils/request.js new file mode 100644 index 0000000..e8730fc --- /dev/null +++ b/exam-vue/src/utils/request.js @@ -0,0 +1,173 @@ +import axios from 'axios' +import { MessageBox, Message } from 'element-ui' +import { Loading } from 'element-ui' +import store from '@/store' +import { getToken } from '@/utils/auth' + +// 请求实例 +const instance = axios.create({ + baseURL: process.env.VUE_APP_BASE_API, + timeout: 60000 +}) + +// 请求前置过滤器 +instance.interceptors.request.use( + config => { + if (store.getters.token) { + config.headers['token'] = getToken() + } + return config + }, + error => { + console.log(error) + return Promise.reject(error) + } +) + +// 响应数据拦截并做通用处理 +instance.interceptors.response.use( + response => { + const res = response.data + + // 下载文件直接返回 + if (res.type === 'application/octet-stream') { + return response + } + + if (res.type === 'application/vnd.ms-excel') { + return response + } + + // 0为正确响应码 + if (res.code !== 0) { + Message({ + message: res.msg || 'Error', + type: 'error', + duration: 5 * 1000 + }) + + // 登录超时响应码 + if (res.code === 10010002) { + // to re-login + MessageBox.confirm('登录超时,请重新登录!', '登录提示', { + confirmButtonText: '重新登录', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + store.dispatch('user/resetToken').then(() => { + location.reload() + }) + }) + } + return Promise.reject(new Error(res.msg || 'Error')) + } else { + return res + } + }, + error => { + console.log('err' + error) + Message({ + message: error.message, + type: 'error', + duration: 5 * 1000 + }) + return Promise.reject(error) + } +) + +/** + * 上传 + * @param url + * @param data + */ +export function upload(url, file, data) { + const formData = new FormData() + formData.append('file', file) + + // 附加数据 + if (data) { + Object.keys(data).forEach((key) => { + formData.append(key, data[key]) + }) + } + + return new Promise((resolve, reject) => { + // 打开 + const loading = Loading.service({ + text: '正在上传数据...', + background: 'rgba(0, 0, 0, 0.7)' + }) + + instance.request({ + url: url, + method: 'post', + data: formData, + timeout: 1200000 + }).then(response => { + console.log(response) + loading.close() + resolve(response) + }).catch(err => { + loading.close() + reject(err) + }) + }) +} + +/** + * 下载 + * @param url + * @param data + */ +export function download(url, data, fileName) { + return new Promise((resolve, reject) => { + // 打开 + const loading = Loading.service({ + text: '正在下载数据...', + background: 'rgba(0, 0, 0, 0.7)' + }) + + instance.request({ + url: url, + method: 'post', + data: data, + timeout: 1200000, + responseType: 'blob' + }).then(res => { + loading.close() + + // 文件下载 + const blob = new Blob([res.data], { + type: 'application/vnd.ms-excel' + }) + + // 获得文件名称 + let link = document.createElement('a') + link.href = URL.createObjectURL(blob) + link.setAttribute('download', fileName) + link.click() + link = null + Message.success('导出成功!') + }).catch(err => { + loading.close() + reject(err) + }) + }) +} + +/** + * 封装post请求 + * @param url + * @param data + * @returns {Promise} + */ +export function post(url, data = {}) { + return new Promise((resolve, reject) => { + instance.post(url, data) + .then(response => { + resolve(response) + }, err => { + reject(err) + }) + }) +} diff --git a/exam-vue/src/utils/scroll-to.js b/exam-vue/src/utils/scroll-to.js new file mode 100644 index 0000000..c5d8e04 --- /dev/null +++ b/exam-vue/src/utils/scroll-to.js @@ -0,0 +1,58 @@ +Math.easeInOutQuad = function(t, b, c, d) { + t /= d / 2 + if (t < 1) { + return c / 2 * t * t + b + } + t-- + return -c / 2 * (t * (t - 2) - 1) + b +} + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +var requestAnimFrame = (function() { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) } +})() + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +function move(amount) { + document.documentElement.scrollTop = amount + document.body.parentNode.scrollTop = amount + document.body.scrollTop = amount +} + +function position() { + return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop +} + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export function scrollTo(to, duration, callback) { + const start = position() + const change = to - start + const increment = 20 + let currentTime = 0 + duration = (typeof (duration) === 'undefined') ? 500 : duration + var animateScroll = function() { + // increment the time + currentTime += increment + // find the value with the quadratic in-out easing function + var val = Math.easeInOutQuad(currentTime, start, change, duration) + // move the document.body + move(val) + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll) + } else { + if (callback && typeof (callback) === 'function') { + // the animation is done so lets callback + callback() + } + } + } + animateScroll() +} diff --git a/exam-vue/src/utils/validate.js b/exam-vue/src/utils/validate.js new file mode 100644 index 0000000..e32c6df --- /dev/null +++ b/exam-vue/src/utils/validate.js @@ -0,0 +1,86 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUsername(str) { + return true +} + +/** + * @param {string} url + * @returns {Boolean} + */ +export function validURL(url) { + const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ + return reg.test(url) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validLowerCase(str) { + const reg = /^[a-z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUpperCase(str) { + const reg = /^[A-Z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validAlphabets(str) { + const reg = /^[A-Za-z]+$/ + return reg.test(str) +} + +/** + * @param {string} email + * @returns {Boolean} + */ +export function validEmail(email) { + const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return reg.test(email) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function isString(str) { + if (typeof str === 'string' || str instanceof String) { + return true + } + return false +} + +/** + * @param {Array} arg + * @returns {Boolean} + */ +export function isArray(arg) { + if (typeof Array.isArray === 'undefined') { + return Object.prototype.toString.call(arg) === '[object Array]' + } + return Array.isArray(arg) +} diff --git a/exam-vue/src/views/dashboard/index.vue b/exam-vue/src/views/dashboard/index.vue new file mode 100644 index 0000000..445aaa4 --- /dev/null +++ b/exam-vue/src/views/dashboard/index.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/exam-vue/src/views/error-page/401.vue b/exam-vue/src/views/error-page/401.vue new file mode 100644 index 0000000..a52ed23 --- /dev/null +++ b/exam-vue/src/views/error-page/401.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/exam-vue/src/views/error-page/404.vue b/exam-vue/src/views/error-page/404.vue new file mode 100644 index 0000000..1791f55 --- /dev/null +++ b/exam-vue/src/views/error-page/404.vue @@ -0,0 +1,228 @@ + + + + + diff --git a/exam-vue/src/views/exam/exam/form.vue b/exam-vue/src/views/exam/exam/form.vue new file mode 100644 index 0000000..37baa8a --- /dev/null +++ b/exam-vue/src/views/exam/exam/form.vue @@ -0,0 +1,469 @@ + + + + diff --git a/exam-vue/src/views/exam/exam/index.vue b/exam-vue/src/views/exam/exam/index.vue new file mode 100644 index 0000000..48c0a0c --- /dev/null +++ b/exam-vue/src/views/exam/exam/index.vue @@ -0,0 +1,177 @@ + + + diff --git a/exam-vue/src/views/login/components/LoginLayout.vue b/exam-vue/src/views/login/components/LoginLayout.vue new file mode 100644 index 0000000..5253a2e --- /dev/null +++ b/exam-vue/src/views/login/components/LoginLayout.vue @@ -0,0 +1,45 @@ + + + + + + diff --git a/exam-vue/src/views/login/index.vue b/exam-vue/src/views/login/index.vue new file mode 100644 index 0000000..bf7a525 --- /dev/null +++ b/exam-vue/src/views/login/index.vue @@ -0,0 +1,103 @@ + + + diff --git a/exam-vue/src/views/login/register.vue b/exam-vue/src/views/login/register.vue new file mode 100644 index 0000000..f02d5bb --- /dev/null +++ b/exam-vue/src/views/login/register.vue @@ -0,0 +1,99 @@ + + + + diff --git a/exam-vue/src/views/paper/exam/components/ExamTimer/index.vue b/exam-vue/src/views/paper/exam/components/ExamTimer/index.vue new file mode 100644 index 0000000..166a116 --- /dev/null +++ b/exam-vue/src/views/paper/exam/components/ExamTimer/index.vue @@ -0,0 +1,63 @@ + + + + diff --git a/exam-vue/src/views/paper/exam/exam.vue b/exam-vue/src/views/paper/exam/exam.vue new file mode 100644 index 0000000..23fcccf --- /dev/null +++ b/exam-vue/src/views/paper/exam/exam.vue @@ -0,0 +1,442 @@ + + + + + + diff --git a/exam-vue/src/views/paper/exam/list.vue b/exam-vue/src/views/paper/exam/list.vue new file mode 100644 index 0000000..443023c --- /dev/null +++ b/exam-vue/src/views/paper/exam/list.vue @@ -0,0 +1,173 @@ + + + diff --git a/exam-vue/src/views/paper/exam/preview.vue b/exam-vue/src/views/paper/exam/preview.vue new file mode 100644 index 0000000..f4ff4c4 --- /dev/null +++ b/exam-vue/src/views/paper/exam/preview.vue @@ -0,0 +1,114 @@ + + + + + + diff --git a/exam-vue/src/views/paper/exam/result.vue b/exam-vue/src/views/paper/exam/result.vue new file mode 100644 index 0000000..1ae0d3c --- /dev/null +++ b/exam-vue/src/views/paper/exam/result.vue @@ -0,0 +1,228 @@ + + + + + + diff --git a/exam-vue/src/views/paper/paper/index.vue b/exam-vue/src/views/paper/paper/index.vue new file mode 100644 index 0000000..8b57eec --- /dev/null +++ b/exam-vue/src/views/paper/paper/index.vue @@ -0,0 +1,219 @@ + + + diff --git a/exam-vue/src/views/profile/components/Account.vue b/exam-vue/src/views/profile/components/Account.vue new file mode 100644 index 0000000..07389fd --- /dev/null +++ b/exam-vue/src/views/profile/components/Account.vue @@ -0,0 +1,49 @@ + + + diff --git a/exam-vue/src/views/profile/components/UserCard.vue b/exam-vue/src/views/profile/components/UserCard.vue new file mode 100644 index 0000000..7d8190f --- /dev/null +++ b/exam-vue/src/views/profile/components/UserCard.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/exam-vue/src/views/profile/index.vue b/exam-vue/src/views/profile/index.vue new file mode 100644 index 0000000..e38e1df --- /dev/null +++ b/exam-vue/src/views/profile/index.vue @@ -0,0 +1,60 @@ + + + diff --git a/exam-vue/src/views/qu/qu/form.vue b/exam-vue/src/views/qu/qu/form.vue new file mode 100644 index 0000000..b2ecee3 --- /dev/null +++ b/exam-vue/src/views/qu/qu/form.vue @@ -0,0 +1,301 @@ + + + + + + diff --git a/exam-vue/src/views/qu/qu/index.vue b/exam-vue/src/views/qu/qu/index.vue new file mode 100644 index 0000000..5d5dcc9 --- /dev/null +++ b/exam-vue/src/views/qu/qu/index.vue @@ -0,0 +1,256 @@ + + + diff --git a/exam-vue/src/views/qu/qu/view.vue b/exam-vue/src/views/qu/qu/view.vue new file mode 100644 index 0000000..910532b --- /dev/null +++ b/exam-vue/src/views/qu/qu/view.vue @@ -0,0 +1,127 @@ + + + + + + diff --git a/exam-vue/src/views/qu/repo/form.vue b/exam-vue/src/views/qu/repo/form.vue new file mode 100644 index 0000000..f36f3c1 --- /dev/null +++ b/exam-vue/src/views/qu/repo/form.vue @@ -0,0 +1,94 @@ + + + diff --git a/exam-vue/src/views/qu/repo/index.vue b/exam-vue/src/views/qu/repo/index.vue new file mode 100644 index 0000000..3cef96e --- /dev/null +++ b/exam-vue/src/views/qu/repo/index.vue @@ -0,0 +1,110 @@ + + + diff --git a/exam-vue/src/views/redirect/index.vue b/exam-vue/src/views/redirect/index.vue new file mode 100644 index 0000000..db4c1d6 --- /dev/null +++ b/exam-vue/src/views/redirect/index.vue @@ -0,0 +1,12 @@ + diff --git a/exam-vue/src/views/sys/config/index.vue b/exam-vue/src/views/sys/config/index.vue new file mode 100644 index 0000000..74e56b4 --- /dev/null +++ b/exam-vue/src/views/sys/config/index.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/exam-vue/src/views/sys/depart/index.vue b/exam-vue/src/views/sys/depart/index.vue new file mode 100644 index 0000000..de70a26 --- /dev/null +++ b/exam-vue/src/views/sys/depart/index.vue @@ -0,0 +1,200 @@ + + + diff --git a/exam-vue/src/views/sys/log/index.vue b/exam-vue/src/views/sys/log/index.vue new file mode 100644 index 0000000..87d4960 --- /dev/null +++ b/exam-vue/src/views/sys/log/index.vue @@ -0,0 +1,103 @@ + + + diff --git a/exam-vue/src/views/sys/role/index.vue b/exam-vue/src/views/sys/role/index.vue new file mode 100644 index 0000000..7902933 --- /dev/null +++ b/exam-vue/src/views/sys/role/index.vue @@ -0,0 +1,76 @@ + + + diff --git a/exam-vue/src/views/sys/user/index.vue b/exam-vue/src/views/sys/user/index.vue new file mode 100644 index 0000000..a1fcf2e --- /dev/null +++ b/exam-vue/src/views/sys/user/index.vue @@ -0,0 +1,231 @@ + + + diff --git a/exam-vue/src/views/user/book/index.vue b/exam-vue/src/views/user/book/index.vue new file mode 100644 index 0000000..df4c9b9 --- /dev/null +++ b/exam-vue/src/views/user/book/index.vue @@ -0,0 +1,103 @@ + + + diff --git a/exam-vue/src/views/user/book/train.vue b/exam-vue/src/views/user/book/train.vue new file mode 100644 index 0000000..0d2d6d7 --- /dev/null +++ b/exam-vue/src/views/user/book/train.vue @@ -0,0 +1,188 @@ + + + + + + diff --git a/exam-vue/src/views/user/exam/index.vue b/exam-vue/src/views/user/exam/index.vue new file mode 100644 index 0000000..23a04fb --- /dev/null +++ b/exam-vue/src/views/user/exam/index.vue @@ -0,0 +1,138 @@ + + + + + diff --git a/exam-vue/src/views/user/exam/my.vue b/exam-vue/src/views/user/exam/my.vue new file mode 100644 index 0000000..2f59a43 --- /dev/null +++ b/exam-vue/src/views/user/exam/my.vue @@ -0,0 +1,138 @@ + + + + + diff --git a/exam-vue/src/views/user/exam/paper.vue b/exam-vue/src/views/user/exam/paper.vue new file mode 100644 index 0000000..c333788 --- /dev/null +++ b/exam-vue/src/views/user/exam/paper.vue @@ -0,0 +1,92 @@ + + + diff --git a/exam-vue/vue.config.js b/exam-vue/vue.config.js new file mode 100644 index 0000000..55e9bd5 --- /dev/null +++ b/exam-vue/vue.config.js @@ -0,0 +1,121 @@ +'use strict' +const path = require('path') +const defaultSettings = require('./src/settings.js') + +function resolve(dir) { + return path.join(__dirname, dir) +} + +const name = defaultSettings.title || '云帆考试系统' +const port = process.env.port || process.env.npm_config_port || 9527 // dev port + +// All configuration item explanations can be find in https://cli.vuejs.org/config/ +module.exports = { + /** + * You will need to set publicPath if you plan to deploy your site under a sub path, + * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/, + * then publicPath should be set to "/bar/". + * In most cases please use '/' !!! + * Detail: https://cli.vuejs.org/config/#publicpath + */ + publicPath: '/', + outputDir: 'dist', + assetsDir: 'static', + lintOnSave: process.env.NODE_ENV === 'development', + productionSourceMap: false, + devServer: { + port: port, + open: true, + overlay: { + warnings: false, + errors: true + } + }, + configureWebpack: { + // provide the app's title in webpack's name field, so that + // it can be accessed in index.html to inject the correct title. + name: name, + resolve: { + alias: { + '@': resolve('src') + } + } + }, + chainWebpack(config) { + config.plugins.delete('preload') // TODO: need test + config.plugins.delete('prefetch') // TODO: need test + + // set svg-sprite-loader + config.module + .rule('svg') + .exclude.add(resolve('src/icons')) + .end() + config.module + .rule('icons') + .test(/\.svg$/) + .include.add(resolve('src/icons')) + .end() + .use('svg-sprite-loader') + .loader('svg-sprite-loader') + .options({ + symbolId: 'icon-[name]' + }) + .end() + + // set preserveWhitespace + config.module + .rule('vue') + .use('vue-loader') + .loader('vue-loader') + .tap(options => { + options.compilerOptions.preserveWhitespace = true + return options + }) + .end() + + config + // https://webpack.js.org/configuration/devtool/#development + .when(process.env.NODE_ENV === 'development', + config => config.devtool('cheap-source-map') + ) + + config + .when(process.env.NODE_ENV !== 'development', + config => { + config + .plugin('ScriptExtHtmlWebpackPlugin') + .after('html') + .use('script-ext-html-webpack-plugin', [{ + // `runtime` must same as runtimeChunk name. default is `runtime` + inline: /runtime\..*\.js$/ + }]) + .end() + config + .optimization.splitChunks({ + chunks: 'all', + cacheGroups: { + libs: { + name: 'chunk-libs', + test: /[\\/]node_modules[\\/]/, + priority: 10, + chunks: 'initial' // only package third parties that are initially dependent + }, + elementUI: { + name: 'chunk-elementUI', // split elementUI into a single package + priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app + test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm + }, + commons: { + name: 'chunk-commons', + test: resolve('src/components'), // can customize your rules + minChunks: 3, // minimum common number + priority: 5, + reuseExistingChunk: true + } + } + }) + config.optimization.runtimeChunk('single') + } + ) + } +} diff --git a/touxiang.png b/touxiang.png new file mode 100644 index 0000000..ddd1d1c Binary files /dev/null and b/touxiang.png differ