[
  {
    "path": ".browserslistrc",
    "content": "> 1%\nlast 2 versions\nnot dead\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules\n/dist\n\n\n# local env files\n.env.local\n.env.*.local\n\n# Log files\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\n\n# Editor directories and files\n.idea\n.vscode\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "README.md",
    "content": "### 豆宝社区项目实战视频教程简介\n本项目实战视频教程全部免费，配套代码完全开源。手把手从零开始搭建一个目前应用最广泛的Springboot+Vue前后端分离多用户社区项目。本项目难度适中，为便于大家学习，每一集视频教程对应在Github上的每一次提交。\n\n### 致谢\n本项目大量借鉴了[极光社区项目](https://github.com/haoyu21/aurora)，在此感谢原作者的无私开源。本项目在其基础上做了一些增删，删除了一些未完成的模块（活动，旅游），新增了评论功能，简化了后端认证与授权功能。最主要的工作是将原项目从零开始开始搭建，各个功能的实现分解成几十步来完成，便于大家更好的学习。\n### 在线体验\n\nhttp://kamiba.gitee.io/doubao_deploy_frontend/\n\n### 代码开源地址\n[前端](https://github.com/songboriceman/doubao_community_frontend)\n[后端](https://github.com/songboriceman/doubao_community_backend)\n\n### 视频教程地址\n[视频教程](https://www.bilibili.com/video/BV1Wz4y1U7vC)\n\n### 项目主要业务及实现的功能\n本项目类似一个简版的掘金这样的技术社区，实现了多个用户注册，登录，发帖，回帖，评论，关注，用户中心等功能。\n\n### 前端技术栈\n Vue\n \n Vuex\n \n Vue Router\n \n Axios\n \n Bulma\n \n Buefy\n \n Element\n \n Vditor\n \n DarkReader\n\n### 后端技术栈\n Spring Boot\n \n Mysql\n \n Mybatis\n \n MyBatis-Plus\n \n Spring Security\n \n JWT\n \n Lombok\n\n### 项目实战大纲：\n01.豆宝社区项目介绍\n\n02.豆宝社区项目所需的基础知识\n\n03.前端项目搭建\n\n04.前端公告板功能实现\n\n05.初始化springboot后端项目\n\n06.初始化后端数据库，springboot配置mybatis连接\n\n07.后端项目目录结构初始化\n\n08.后端公告板接口功能实现01\n\n09.后端公告板接口功能实现02\n\n10.前端端公告板接口功能实现\n\n11.实现跨域，前后端接口联调\n\n12.每日一句功能前端界面实现01\n\n13.每日一句功能前端界面实现02\n\n14.每日一句功能前端接口实现\n\n15.每日一句功能后端接口实现\n\n16.（非常重要）善用github提交记录进行项目学习\n\n17.推广链接功能 前后端实现\n\n18.用户注册前端实现\n\n19.用户注册后端实现\n\n20.jwt以及web通信流程\n\n21.用户登录后端实现\n\n22.vuex简介\n\n23.js-cookie介绍\n\n24.用户登录前端实现\n\n25.前端侧边栏，马上入驻，社区登入功能\n\n26.前端在axios请求拦截器中在请求头中加入jwt\n\n27.后端设置请求拦截器检查用户请求头中是否包含jwt01\n\n28.后端设置请求拦截器检查用户请求头中是否包含jwt02\n\n29.前端header实现01\n\n30.前端header实现02\n\n31.退出登录\n\n32.前端页脚功能实现\n\n33.帖子列表功能前端\n\n34.帖子列表功能后端\n\n35.帖子分页功能实现\n\n36.前端实现发表帖子功能\n\n37.后端实现发表帖子功能\n\n38.前端实现帖子详情功能\n\n39.后端实现帖子详情功能\n\n40.帖子详情右侧边栏帖子作者详情功能实现（前端）\n\n41.帖子详情右侧边栏帖子作者详情用户关注功能实现（后端）\n\n42.随便看看模块前端实现\n\n43.随便看看模块后端实现\n\n44.评论列表功能前端实现\n\n45.评论列表功能后端实现\n\n46.添加评论功能前端实现\n\n47.添加评论功能后端实现\n\n48.帖子更新与删除功能前后端实现\n\n49.显示某个标签的全部文章功能前端\n\n50.显示某个标签的全部文章功能后端实现\n\n51.帖子搜索功能前端实现\n\n52.帖子搜索功能前端实现\n\n53.用户中心功能前端实现\n\n54.用户中心功能前端实现\n\n55.用户个人信息修改\n\n56.前端发帖，留言等页面登录权限验证\n\n57.后端发帖，留言等需要登录页面的权限验证\n\n58.项目总结及遗留问题说明\n\n59.（重要的说明）如何利用github上开源的项目代码提交记录更有效的学习本项目\n\n### 豆约翰团队：\n一群热爱分享技术，拥有多年开发经验及培训经验的老司机组成\n\n### 擅长的领域：\njava，python，前端，c++，.net\n\n### 项目部分截图\n#### PC\n#### 首页\n![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/30def8066ef8414ea4230f468139df09~tplv-k3u1fbpfcp-zoom-1.image)\n#### 发表文章\n![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0b27ea7463b4459ebffae7963926e0d7~tplv-k3u1fbpfcp-zoom-1.image)\n\n#### 文章详情及评论页面\n![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5474e766b4374b2dacd11f0dbe933f0d~tplv-k3u1fbpfcp-zoom-1.image)\n\n#### 个人中心\n![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5175ad7e76c245f8a76cd828117507c9~tplv-k3u1fbpfcp-zoom-1.image)\n#### 用户设置\n![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6e7a774f6bd14a7f9f573718844f0055~tplv-k3u1fbpfcp-zoom-1.image)\n#### 移动端\n#### 首页\n![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4d198980de974b65b642582db908d355~tplv-k3u1fbpfcp-zoom-1.image)\n#### 用户中心\n![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1b31f1509f854616982c726405a8835e~tplv-k3u1fbpfcp-zoom-1.image)\n#### 详情页\n![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e548602a256f4e16b6d51e31abccd7b6~tplv-k3u1fbpfcp-zoom-1.image)\n\n### 技术讨论群\n\n为方便同学们讨论项目中的技术，建了一个QQ群：\n\n![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9e0650ef53de41f4aed16d3644f5be34~tplv-k3u1fbpfcp-zoom-1.image)\n\n\n\n"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = {\n  presets: [\n    '@vue/cli-plugin-babel/preset'\n  ]\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"doubao_community_frontend\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"serve\": \"vue-cli-service serve\",\n    \"build\": \"vue-cli-service build\"\n  },\n  \"dependencies\": {\n    \"axios\": \"^0.21.1\",\n    \"buefy\": \"^0.9.4\",\n    \"core-js\": \"^3.6.5\",\n    \"darkreader\": \"^4.9.27\",\n    \"date-fns\": \"^2.17.0\",\n    \"dayjs\": \"^1.10.4\",\n    \"element-ui\": \"^2.15.0\",\n    \"js-cookie\": \"^2.2.1\",\n    \"nprogress\": \"^0.2.0\",\n    \"vditor\": \"^3.8.1\",\n    \"vue\": \"^2.6.11\",\n    \"vue-router\": \"^3.2.0\",\n    \"vuex\": \"^3.4.0\"\n  },\n  \"devDependencies\": {\n    \"@vue/cli-plugin-babel\": \"~4.5.0\",\n    \"@vue/cli-plugin-router\": \"~4.5.0\",\n    \"@vue/cli-plugin-vuex\": \"~4.5.0\",\n    \"@vue/cli-service\": \"~4.5.0\",\n    \"vue-template-compiler\": \"^2.6.11\"\n  }\n}\n"
  },
  {
    "path": "public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n    <link rel=\"icon\" href=\"<%= BASE_URL %>favicon.ico\">\n    <title><%= htmlWebpackPlugin.options.title %></title>\n  </head>\n  <body>\n    <noscript>\n      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>\n    </noscript>\n    <div id=\"app\"></div>\n    <!-- built files will be auto injected -->\n  </body>\n</html>\n"
  },
  {
    "path": "src/App.vue",
    "content": "<template>\n  <div>\n    <div class=\"mb-5\">\n      <Header></Header>\n    </div>\n\n    <div class=\"container context\">\n        <router-view :key=\"this.$route.fullPath\"></router-view>\n    </div>\n\n    <div>\n      <Footer></Footer>\n    </div>\n  </div>\n</template>\n\n<script>\nimport Header from \"@/components/Layout/Header\";\nimport Footer from \"@/components/Layout/Footer\";\n\nexport default {\n  name: \"App\",\n  components: { Header, Footer },\n};\n</script>\n\n<style scoped>\n.container {\n  min-height: 500px;\n}\n</style>\n"
  },
  {
    "path": "src/api/auth/auth.js",
    "content": "import request from '@/utils/request'\n\n// 注册\nexport function userRegister(userDTO) {\n  return request({\n    url: '/ums/user/register',\n    method: 'post',\n    data: userDTO\n  })\n}\n\n// 前台用户登录\nexport function login(data) {\n  return request({\n    url: '/ums/user/login',\n    method: 'post',\n    data\n  })\n}\n// 登录后获取前台用户信息\nexport function getUserInfo() {\n  return request({\n    url: '/ums/user/info',\n    method: 'get'\n  })\n}\n// 前台用户注销\nexport function logout() {\n  return request({\n    url: '/ums/user/logout'\n  })\n}"
  },
  {
    "path": "src/api/billboard.js",
    "content": "import request from '@/utils/request'\n\nexport function getBillboard() {\n  return request({\n    url: '/billboard/show',\n    method: 'get'\n  })\n}"
  },
  {
    "path": "src/api/comment.js",
    "content": "import request from '@/utils/request'\n\nexport function fetchCommentsByTopicId(topic_Id) {\n  return request({\n    url: '/comment/get_comments',\n    method: 'get',\n    params: {\n      topicid: topic_Id\n    }\n  })\n}\n\nexport function pushComment(data) {\n  return request({\n    url: '/comment/add_comment',\n    method: 'post',\n    data: data\n  })\n}\n\n"
  },
  {
    "path": "src/api/follow.js",
    "content": "import request from '@/utils/request'\n\n// 关注\nexport function follow(id) {\n  return request(({\n    url: `/relationship/subscribe/${id}`,\n    method: 'get'\n  }))\n}\n\n// 关注\nexport function unFollow(id) {\n  return request(({\n    url: `/relationship/unsubscribe/${id}`,\n    method: 'get'\n  }))\n}\n\n// 验证是否关注\nexport function hasFollow(topicUserId) {\n  return request(({\n    url: `/relationship/validate/${topicUserId}`,\n    method: 'get'\n  }))\n}\n"
  },
  {
    "path": "src/api/post.js",
    "content": "import request from '@/utils/request'\n\n// 列表\nexport function getList(pageNo, size, tab) {\n  return request(({\n    url: '/post/list',\n    method: 'get',\n    params: { pageNo: pageNo, size: size, tab: tab }\n  }))\n}\n\n// 发布\nexport function post(topic) {\n  return request({\n    url: '/post/create',\n    method: 'post',\n    data: topic\n  })\n}\n\n// 浏览\nexport function getTopic(id) {\n  return request({\n    url: `/post`,\n    method: 'get',\n    params: {\n      id: id\n    }\n  })\n}\n// 获取详情页推荐\nexport function getRecommendTopics(id) {\n  return request({\n    url: '/post/recommend',\n    method: 'get',\n    params: {\n      topicId: id\n    }\n  })\n}\n\nexport function update(topic) {\n  return request({\n    url: '/post/update',\n    method: 'post',\n    data: topic\n  })\n}\n\nexport function deleteTopic(id) {\n  return request({\n    url: `/post/delete/${id}`,\n    method: 'delete'\n  })\n}\n\n"
  },
  {
    "path": "src/api/promote.js",
    "content": "import request from '@/utils/request'\n\n// 获取推广\nexport function getList() {\n  return request(({\n    url: '/promotion/all',\n    method: 'get'\n  }))\n}\n"
  },
  {
    "path": "src/api/search.js",
    "content": "import request from '@/utils/request'\n\n// 关键词检索\nexport function searchByKeyword(query) {\n  return request({\n    url: `/search`,\n    method: 'get',\n    params: {\n      keyword: query.keyword,\n      pageNum: query.pageNum,\n      pageSize: query.pageSize\n    }\n  })\n}\n"
  },
  {
    "path": "src/api/tag.js",
    "content": "import request from '@/utils/request'\n\nexport function getTopicsByTag(paramMap) {\n  return request({\n    url: '/tag/' + paramMap.name,\n    method: 'get',\n    params: {\n      page: paramMap.page,\n      size: paramMap.size\n    }\n  })\n}\n"
  },
  {
    "path": "src/api/tip.js",
    "content": "import request from '@/utils/request'\n\nexport function getTodayTip() {\n  return request({\n    url: '/tip/today',\n    method: 'get'\n  })\n}"
  },
  {
    "path": "src/api/user.js",
    "content": "import request from '@/utils/request'\n\n// 用户主页\nexport function getInfoByName(username, page, size) {\n  return request({\n    url: '/ums/user/' + username,\n    method: 'get',\n    params: {\n      pageNo: page,\n      size: size\n    }\n  })\n}\n// 用户主页\nexport function getInfo() {\n  return request({\n    url: '/ums/user/info',\n    method: 'get'\n  })\n}\n// 更新\nexport function update(user) {\n  return request({\n    url: '/ums/user/update',\n    method: 'post',\n    data: user\n  })\n}\n\n\n"
  },
  {
    "path": "src/assets/app.css",
    "content": "* {\n  margin: 0;\n  padding: 0;\n}\n\nbody,\nhtml {\n  background-color: #f6f6f6;\n  color: black;\n  width: 100%;\n  font-size: 14px;\n  letter-spacing: 0.03em;\n  font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC,\n    Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, WenQuanYi Micro Hei,\n    sans-serif, Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji,\n    Segoe UI Symbol, Android Emoji, EmojiSymbols;\n}\n\n/*背景图*/\n/*body {*/\n/*    background-image: url('https://api.mz-moe.cn/img.php');*/\n/*    background-repeat: round;*/\n/*}*/\n\n@media (min-width: 768px) {\n  .container {\n    width: 760px;\n  }\n}\n\n@media (min-width: 992px) {\n  .container {\n    width: 980px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .container {\n    width: 1080px;\n  }\n}\n\n/*滚动条*/\n::-webkit-scrollbar {\n  width: 10px;\n  height: 10px;\n  /**/\n}\n\n::-webkit-scrollbar-track {\n  background: rgb(239, 239, 239);\n  border-radius: 2px;\n}\n\n::-webkit-scrollbar-thumb {\n  background: #bfbfbf;\n  border-radius: 10px;\n}\n\n::-webkit-scrollbar-corner {\n  background: #179a16;\n}\n\n.header {\n  position: fixed;\n  z-index: 89;\n  top: 0;\n  width: 100%;\n  min-width: 1032px;\n  background: #fff;\n  box-shadow: 0 1px 0px rgba(26, 26, 26, 0.1);\n  height: 53px;\n  font-size: 16px;\n}\n\na {\n  color: #1d1d1d;\n  text-decoration: none;\n}\n\na:hover {\n  color: #f60;\n  text-decoration: none !important;\n}\n\n.shadow-1 {\n  box-shadow: 0 0.5em 1em -0.125em rgba(10, 10, 10, 0.1),\n    0 0 0 1px rgba(10, 10, 10, 0.02);\n}\n\n.navbar-dropdown {\n  font-size: 15px;\n}\n\n/*统一卡片样式*/\n.el-card {\n  /*border-radius: 3px !important;*/\n  margin-bottom: 16px;\n  /*border: none;*/\n}\n\n.my-card {\n  cursor: pointer;\n  transition: all 0.1s ease-in-out;\n  position: relative;\n  overflow: hidden;\n}\n\n.my-card:hover {\n  transform: scale(1.03);\n}\n\n::selection {\n  text-shadow: none;\n  background: rgba(67, 135, 244, 0.56);\n}\n\n/* 搜索框 */\n.search-bar input {\n  border: none;\n  box-shadow: none;\n}\n\n/*按钮居中*/\n.button-center {\n  display: block;\n  margin: 0 auto;\n}\n\n.ellipsis {\n  display: block;\n  display: -webkit-box;\n  margin: 0 auto;\n  line-height: 1.4;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.is-ellipsis-1 {\n  -webkit-line-clamp: 1;\n}\n\n.is-ellipsis-2 {\n  -webkit-line-clamp: 2;\n}\n\n.is-ellipsis-3 {\n  -webkit-line-clamp: 3;\n}\n"
  },
  {
    "path": "src/assets/plugins/font-awesome-4.7.0/css/font-awesome.css",
    "content": "/*!\n *  Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome\n *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)\n */\n/* FONT PATH\n * -------------------------- */\n@font-face {\n  font-family: 'FontAwesome';\n  src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');\n  src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');\n  font-weight: normal;\n  font-style: normal;\n}\n.fa {\n  display: inline-block;\n  font: normal normal normal 14px/1 FontAwesome;\n  font-size: inherit;\n  text-rendering: auto;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n/* makes the font 33% larger relative to the icon container */\n.fa-lg {\n  font-size: 1.33333333em;\n  line-height: 0.75em;\n  vertical-align: -15%;\n}\n.fa-2x {\n  font-size: 2em;\n}\n.fa-3x {\n  font-size: 3em;\n}\n.fa-4x {\n  font-size: 4em;\n}\n.fa-5x {\n  font-size: 5em;\n}\n.fa-fw {\n  width: 1.28571429em;\n  text-align: center;\n}\n.fa-ul {\n  padding-left: 0;\n  margin-left: 2.14285714em;\n  list-style-type: none;\n}\n.fa-ul > li {\n  position: relative;\n}\n.fa-li {\n  position: absolute;\n  left: -2.14285714em;\n  width: 2.14285714em;\n  top: 0.14285714em;\n  text-align: center;\n}\n.fa-li.fa-lg {\n  left: -1.85714286em;\n}\n.fa-border {\n  padding: .2em .25em .15em;\n  border: solid 0.08em #eeeeee;\n  border-radius: .1em;\n}\n.fa-pull-left {\n  float: left;\n}\n.fa-pull-right {\n  float: right;\n}\n.fa.fa-pull-left {\n  margin-right: .3em;\n}\n.fa.fa-pull-right {\n  margin-left: .3em;\n}\n/* Deprecated as of 4.4.0 */\n.pull-right {\n  float: right;\n}\n.pull-left {\n  float: left;\n}\n.fa.pull-left {\n  margin-right: .3em;\n}\n.fa.pull-right {\n  margin-left: .3em;\n}\n.fa-spin {\n  -webkit-animation: fa-spin 2s infinite linear;\n  animation: fa-spin 2s infinite linear;\n}\n.fa-pulse {\n  -webkit-animation: fa-spin 1s infinite steps(8);\n  animation: fa-spin 1s infinite steps(8);\n}\n@-webkit-keyframes fa-spin {\n  0% {\n    -webkit-transform: rotate(0deg);\n    transform: rotate(0deg);\n  }\n  100% {\n    -webkit-transform: rotate(359deg);\n    transform: rotate(359deg);\n  }\n}\n@keyframes fa-spin {\n  0% {\n    -webkit-transform: rotate(0deg);\n    transform: rotate(0deg);\n  }\n  100% {\n    -webkit-transform: rotate(359deg);\n    transform: rotate(359deg);\n  }\n}\n.fa-rotate-90 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)\";\n  -webkit-transform: rotate(90deg);\n  -ms-transform: rotate(90deg);\n  transform: rotate(90deg);\n}\n.fa-rotate-180 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)\";\n  -webkit-transform: rotate(180deg);\n  -ms-transform: rotate(180deg);\n  transform: rotate(180deg);\n}\n.fa-rotate-270 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)\";\n  -webkit-transform: rotate(270deg);\n  -ms-transform: rotate(270deg);\n  transform: rotate(270deg);\n}\n.fa-flip-horizontal {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)\";\n  -webkit-transform: scale(-1, 1);\n  -ms-transform: scale(-1, 1);\n  transform: scale(-1, 1);\n}\n.fa-flip-vertical {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)\";\n  -webkit-transform: scale(1, -1);\n  -ms-transform: scale(1, -1);\n  transform: scale(1, -1);\n}\n:root .fa-rotate-90,\n:root .fa-rotate-180,\n:root .fa-rotate-270,\n:root .fa-flip-horizontal,\n:root .fa-flip-vertical {\n  filter: none;\n}\n.fa-stack {\n  position: relative;\n  display: inline-block;\n  width: 2em;\n  height: 2em;\n  line-height: 2em;\n  vertical-align: middle;\n}\n.fa-stack-1x,\n.fa-stack-2x {\n  position: absolute;\n  left: 0;\n  width: 100%;\n  text-align: center;\n}\n.fa-stack-1x {\n  line-height: inherit;\n}\n.fa-stack-2x {\n  font-size: 2em;\n}\n.fa-inverse {\n  color: #ffffff;\n}\n/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen\n   readers do not read off random characters that represent icons */\n.fa-glass:before {\n  content: \"\\f000\";\n}\n.fa-music:before {\n  content: \"\\f001\";\n}\n.fa-search:before {\n  content: \"\\f002\";\n}\n.fa-envelope-o:before {\n  content: \"\\f003\";\n}\n.fa-heart:before {\n  content: \"\\f004\";\n}\n.fa-star:before {\n  content: \"\\f005\";\n}\n.fa-star-o:before {\n  content: \"\\f006\";\n}\n.fa-user:before {\n  content: \"\\f007\";\n}\n.fa-film:before {\n  content: \"\\f008\";\n}\n.fa-th-large:before {\n  content: \"\\f009\";\n}\n.fa-th:before {\n  content: \"\\f00a\";\n}\n.fa-th-list:before {\n  content: \"\\f00b\";\n}\n.fa-check:before {\n  content: \"\\f00c\";\n}\n.fa-remove:before,\n.fa-close:before,\n.fa-times:before {\n  content: \"\\f00d\";\n}\n.fa-search-plus:before {\n  content: \"\\f00e\";\n}\n.fa-search-minus:before {\n  content: \"\\f010\";\n}\n.fa-power-off:before {\n  content: \"\\f011\";\n}\n.fa-signal:before {\n  content: \"\\f012\";\n}\n.fa-gear:before,\n.fa-cog:before {\n  content: \"\\f013\";\n}\n.fa-trash-o:before {\n  content: \"\\f014\";\n}\n.fa-home:before {\n  content: \"\\f015\";\n}\n.fa-file-o:before {\n  content: \"\\f016\";\n}\n.fa-clock-o:before {\n  content: \"\\f017\";\n}\n.fa-road:before {\n  content: \"\\f018\";\n}\n.fa-download:before {\n  content: \"\\f019\";\n}\n.fa-arrow-circle-o-down:before {\n  content: \"\\f01a\";\n}\n.fa-arrow-circle-o-up:before {\n  content: \"\\f01b\";\n}\n.fa-inbox:before {\n  content: \"\\f01c\";\n}\n.fa-play-circle-o:before {\n  content: \"\\f01d\";\n}\n.fa-rotate-right:before,\n.fa-repeat:before {\n  content: \"\\f01e\";\n}\n.fa-refresh:before {\n  content: \"\\f021\";\n}\n.fa-list-alt:before {\n  content: \"\\f022\";\n}\n.fa-lock:before {\n  content: \"\\f023\";\n}\n.fa-flag:before {\n  content: \"\\f024\";\n}\n.fa-headphones:before {\n  content: \"\\f025\";\n}\n.fa-volume-off:before {\n  content: \"\\f026\";\n}\n.fa-volume-down:before {\n  content: \"\\f027\";\n}\n.fa-volume-up:before {\n  content: \"\\f028\";\n}\n.fa-qrcode:before {\n  content: \"\\f029\";\n}\n.fa-barcode:before {\n  content: \"\\f02a\";\n}\n.fa-tag:before {\n  content: \"\\f02b\";\n}\n.fa-tags:before {\n  content: \"\\f02c\";\n}\n.fa-book:before {\n  content: \"\\f02d\";\n}\n.fa-bookmark:before {\n  content: \"\\f02e\";\n}\n.fa-print:before {\n  content: \"\\f02f\";\n}\n.fa-camera:before {\n  content: \"\\f030\";\n}\n.fa-font:before {\n  content: \"\\f031\";\n}\n.fa-bold:before {\n  content: \"\\f032\";\n}\n.fa-italic:before {\n  content: \"\\f033\";\n}\n.fa-text-height:before {\n  content: \"\\f034\";\n}\n.fa-text-width:before {\n  content: \"\\f035\";\n}\n.fa-align-left:before {\n  content: \"\\f036\";\n}\n.fa-align-center:before {\n  content: \"\\f037\";\n}\n.fa-align-right:before {\n  content: \"\\f038\";\n}\n.fa-align-justify:before {\n  content: \"\\f039\";\n}\n.fa-list:before {\n  content: \"\\f03a\";\n}\n.fa-dedent:before,\n.fa-outdent:before {\n  content: \"\\f03b\";\n}\n.fa-indent:before {\n  content: \"\\f03c\";\n}\n.fa-video-camera:before {\n  content: \"\\f03d\";\n}\n.fa-photo:before,\n.fa-image:before,\n.fa-picture-o:before {\n  content: \"\\f03e\";\n}\n.fa-pencil:before {\n  content: \"\\f040\";\n}\n.fa-map-marker:before {\n  content: \"\\f041\";\n}\n.fa-adjust:before {\n  content: \"\\f042\";\n}\n.fa-tint:before {\n  content: \"\\f043\";\n}\n.fa-edit:before,\n.fa-pencil-square-o:before {\n  content: \"\\f044\";\n}\n.fa-share-square-o:before {\n  content: \"\\f045\";\n}\n.fa-check-square-o:before {\n  content: \"\\f046\";\n}\n.fa-arrows:before {\n  content: \"\\f047\";\n}\n.fa-step-backward:before {\n  content: \"\\f048\";\n}\n.fa-fast-backward:before {\n  content: \"\\f049\";\n}\n.fa-backward:before {\n  content: \"\\f04a\";\n}\n.fa-play:before {\n  content: \"\\f04b\";\n}\n.fa-pause:before {\n  content: \"\\f04c\";\n}\n.fa-stop:before {\n  content: \"\\f04d\";\n}\n.fa-forward:before {\n  content: \"\\f04e\";\n}\n.fa-fast-forward:before {\n  content: \"\\f050\";\n}\n.fa-step-forward:before {\n  content: \"\\f051\";\n}\n.fa-eject:before {\n  content: \"\\f052\";\n}\n.fa-chevron-left:before {\n  content: \"\\f053\";\n}\n.fa-chevron-right:before {\n  content: \"\\f054\";\n}\n.fa-plus-circle:before {\n  content: \"\\f055\";\n}\n.fa-minus-circle:before {\n  content: \"\\f056\";\n}\n.fa-times-circle:before {\n  content: \"\\f057\";\n}\n.fa-check-circle:before {\n  content: \"\\f058\";\n}\n.fa-question-circle:before {\n  content: \"\\f059\";\n}\n.fa-info-circle:before {\n  content: \"\\f05a\";\n}\n.fa-crosshairs:before {\n  content: \"\\f05b\";\n}\n.fa-times-circle-o:before {\n  content: \"\\f05c\";\n}\n.fa-check-circle-o:before {\n  content: \"\\f05d\";\n}\n.fa-ban:before {\n  content: \"\\f05e\";\n}\n.fa-arrow-left:before {\n  content: \"\\f060\";\n}\n.fa-arrow-right:before {\n  content: \"\\f061\";\n}\n.fa-arrow-up:before {\n  content: \"\\f062\";\n}\n.fa-arrow-down:before {\n  content: \"\\f063\";\n}\n.fa-mail-forward:before,\n.fa-share:before {\n  content: \"\\f064\";\n}\n.fa-expand:before {\n  content: \"\\f065\";\n}\n.fa-compress:before {\n  content: \"\\f066\";\n}\n.fa-plus:before {\n  content: \"\\f067\";\n}\n.fa-minus:before {\n  content: \"\\f068\";\n}\n.fa-asterisk:before {\n  content: \"\\f069\";\n}\n.fa-exclamation-circle:before {\n  content: \"\\f06a\";\n}\n.fa-gift:before {\n  content: \"\\f06b\";\n}\n.fa-leaf:before {\n  content: \"\\f06c\";\n}\n.fa-fire:before {\n  content: \"\\f06d\";\n}\n.fa-eye:before {\n  content: \"\\f06e\";\n}\n.fa-eye-slash:before {\n  content: \"\\f070\";\n}\n.fa-warning:before,\n.fa-exclamation-triangle:before {\n  content: \"\\f071\";\n}\n.fa-plane:before {\n  content: \"\\f072\";\n}\n.fa-calendar:before {\n  content: \"\\f073\";\n}\n.fa-random:before {\n  content: \"\\f074\";\n}\n.fa-comment:before {\n  content: \"\\f075\";\n}\n.fa-magnet:before {\n  content: \"\\f076\";\n}\n.fa-chevron-up:before {\n  content: \"\\f077\";\n}\n.fa-chevron-down:before {\n  content: \"\\f078\";\n}\n.fa-retweet:before {\n  content: \"\\f079\";\n}\n.fa-shopping-cart:before {\n  content: \"\\f07a\";\n}\n.fa-folder:before {\n  content: \"\\f07b\";\n}\n.fa-folder-open:before {\n  content: \"\\f07c\";\n}\n.fa-arrows-v:before {\n  content: \"\\f07d\";\n}\n.fa-arrows-h:before {\n  content: \"\\f07e\";\n}\n.fa-bar-chart-o:before,\n.fa-bar-chart:before {\n  content: \"\\f080\";\n}\n.fa-twitter-square:before {\n  content: \"\\f081\";\n}\n.fa-facebook-square:before {\n  content: \"\\f082\";\n}\n.fa-camera-retro:before {\n  content: \"\\f083\";\n}\n.fa-key:before {\n  content: \"\\f084\";\n}\n.fa-gears:before,\n.fa-cogs:before {\n  content: \"\\f085\";\n}\n.fa-comments:before {\n  content: \"\\f086\";\n}\n.fa-thumbs-o-up:before {\n  content: \"\\f087\";\n}\n.fa-thumbs-o-down:before {\n  content: \"\\f088\";\n}\n.fa-star-half:before {\n  content: \"\\f089\";\n}\n.fa-heart-o:before {\n  content: \"\\f08a\";\n}\n.fa-sign-out:before {\n  content: \"\\f08b\";\n}\n.fa-linkedin-square:before {\n  content: \"\\f08c\";\n}\n.fa-thumb-tack:before {\n  content: \"\\f08d\";\n}\n.fa-external-link:before {\n  content: \"\\f08e\";\n}\n.fa-sign-in:before {\n  content: \"\\f090\";\n}\n.fa-trophy:before {\n  content: \"\\f091\";\n}\n.fa-github-square:before {\n  content: \"\\f092\";\n}\n.fa-upload:before {\n  content: \"\\f093\";\n}\n.fa-lemon-o:before {\n  content: \"\\f094\";\n}\n.fa-phone:before {\n  content: \"\\f095\";\n}\n.fa-square-o:before {\n  content: \"\\f096\";\n}\n.fa-bookmark-o:before {\n  content: \"\\f097\";\n}\n.fa-phone-square:before {\n  content: \"\\f098\";\n}\n.fa-twitter:before {\n  content: \"\\f099\";\n}\n.fa-facebook-f:before,\n.fa-facebook:before {\n  content: \"\\f09a\";\n}\n.fa-github:before {\n  content: \"\\f09b\";\n}\n.fa-unlock:before {\n  content: \"\\f09c\";\n}\n.fa-credit-card:before {\n  content: \"\\f09d\";\n}\n.fa-feed:before,\n.fa-rss:before {\n  content: \"\\f09e\";\n}\n.fa-hdd-o:before {\n  content: \"\\f0a0\";\n}\n.fa-bullhorn:before {\n  content: \"\\f0a1\";\n}\n.fa-bell:before {\n  content: \"\\f0f3\";\n}\n.fa-certificate:before {\n  content: \"\\f0a3\";\n}\n.fa-hand-o-right:before {\n  content: \"\\f0a4\";\n}\n.fa-hand-o-left:before {\n  content: \"\\f0a5\";\n}\n.fa-hand-o-up:before {\n  content: \"\\f0a6\";\n}\n.fa-hand-o-down:before {\n  content: \"\\f0a7\";\n}\n.fa-arrow-circle-left:before {\n  content: \"\\f0a8\";\n}\n.fa-arrow-circle-right:before {\n  content: \"\\f0a9\";\n}\n.fa-arrow-circle-up:before {\n  content: \"\\f0aa\";\n}\n.fa-arrow-circle-down:before {\n  content: \"\\f0ab\";\n}\n.fa-globe:before {\n  content: \"\\f0ac\";\n}\n.fa-wrench:before {\n  content: \"\\f0ad\";\n}\n.fa-tasks:before {\n  content: \"\\f0ae\";\n}\n.fa-filter:before {\n  content: \"\\f0b0\";\n}\n.fa-briefcase:before {\n  content: \"\\f0b1\";\n}\n.fa-arrows-alt:before {\n  content: \"\\f0b2\";\n}\n.fa-group:before,\n.fa-users:before {\n  content: \"\\f0c0\";\n}\n.fa-chain:before,\n.fa-link:before {\n  content: \"\\f0c1\";\n}\n.fa-cloud:before {\n  content: \"\\f0c2\";\n}\n.fa-flask:before {\n  content: \"\\f0c3\";\n}\n.fa-cut:before,\n.fa-scissors:before {\n  content: \"\\f0c4\";\n}\n.fa-copy:before,\n.fa-files-o:before {\n  content: \"\\f0c5\";\n}\n.fa-paperclip:before {\n  content: \"\\f0c6\";\n}\n.fa-save:before,\n.fa-floppy-o:before {\n  content: \"\\f0c7\";\n}\n.fa-square:before {\n  content: \"\\f0c8\";\n}\n.fa-navicon:before,\n.fa-reorder:before,\n.fa-bars:before {\n  content: \"\\f0c9\";\n}\n.fa-list-ul:before {\n  content: \"\\f0ca\";\n}\n.fa-list-ol:before {\n  content: \"\\f0cb\";\n}\n.fa-strikethrough:before {\n  content: \"\\f0cc\";\n}\n.fa-underline:before {\n  content: \"\\f0cd\";\n}\n.fa-table:before {\n  content: \"\\f0ce\";\n}\n.fa-magic:before {\n  content: \"\\f0d0\";\n}\n.fa-truck:before {\n  content: \"\\f0d1\";\n}\n.fa-pinterest:before {\n  content: \"\\f0d2\";\n}\n.fa-pinterest-square:before {\n  content: \"\\f0d3\";\n}\n.fa-google-plus-square:before {\n  content: \"\\f0d4\";\n}\n.fa-google-plus:before {\n  content: \"\\f0d5\";\n}\n.fa-money:before {\n  content: \"\\f0d6\";\n}\n.fa-caret-down:before {\n  content: \"\\f0d7\";\n}\n.fa-caret-up:before {\n  content: \"\\f0d8\";\n}\n.fa-caret-left:before {\n  content: \"\\f0d9\";\n}\n.fa-caret-right:before {\n  content: \"\\f0da\";\n}\n.fa-columns:before {\n  content: \"\\f0db\";\n}\n.fa-unsorted:before,\n.fa-sort:before {\n  content: \"\\f0dc\";\n}\n.fa-sort-down:before,\n.fa-sort-desc:before {\n  content: \"\\f0dd\";\n}\n.fa-sort-up:before,\n.fa-sort-asc:before {\n  content: \"\\f0de\";\n}\n.fa-envelope:before {\n  content: \"\\f0e0\";\n}\n.fa-linkedin:before {\n  content: \"\\f0e1\";\n}\n.fa-rotate-left:before,\n.fa-undo:before {\n  content: \"\\f0e2\";\n}\n.fa-legal:before,\n.fa-gavel:before {\n  content: \"\\f0e3\";\n}\n.fa-dashboard:before,\n.fa-tachometer:before {\n  content: \"\\f0e4\";\n}\n.fa-comment-o:before {\n  content: \"\\f0e5\";\n}\n.fa-comments-o:before {\n  content: \"\\f0e6\";\n}\n.fa-flash:before,\n.fa-bolt:before {\n  content: \"\\f0e7\";\n}\n.fa-sitemap:before {\n  content: \"\\f0e8\";\n}\n.fa-umbrella:before {\n  content: \"\\f0e9\";\n}\n.fa-paste:before,\n.fa-clipboard:before {\n  content: \"\\f0ea\";\n}\n.fa-lightbulb-o:before {\n  content: \"\\f0eb\";\n}\n.fa-exchange:before {\n  content: \"\\f0ec\";\n}\n.fa-cloud-download:before {\n  content: \"\\f0ed\";\n}\n.fa-cloud-upload:before {\n  content: \"\\f0ee\";\n}\n.fa-user-md:before {\n  content: \"\\f0f0\";\n}\n.fa-stethoscope:before {\n  content: \"\\f0f1\";\n}\n.fa-suitcase:before {\n  content: \"\\f0f2\";\n}\n.fa-bell-o:before {\n  content: \"\\f0a2\";\n}\n.fa-coffee:before {\n  content: \"\\f0f4\";\n}\n.fa-cutlery:before {\n  content: \"\\f0f5\";\n}\n.fa-file-text-o:before {\n  content: \"\\f0f6\";\n}\n.fa-building-o:before {\n  content: \"\\f0f7\";\n}\n.fa-hospital-o:before {\n  content: \"\\f0f8\";\n}\n.fa-ambulance:before {\n  content: \"\\f0f9\";\n}\n.fa-medkit:before {\n  content: \"\\f0fa\";\n}\n.fa-fighter-jet:before {\n  content: \"\\f0fb\";\n}\n.fa-beer:before {\n  content: \"\\f0fc\";\n}\n.fa-h-square:before {\n  content: \"\\f0fd\";\n}\n.fa-plus-square:before {\n  content: \"\\f0fe\";\n}\n.fa-angle-double-left:before {\n  content: \"\\f100\";\n}\n.fa-angle-double-right:before {\n  content: \"\\f101\";\n}\n.fa-angle-double-up:before {\n  content: \"\\f102\";\n}\n.fa-angle-double-down:before {\n  content: \"\\f103\";\n}\n.fa-angle-left:before {\n  content: \"\\f104\";\n}\n.fa-angle-right:before {\n  content: \"\\f105\";\n}\n.fa-angle-up:before {\n  content: \"\\f106\";\n}\n.fa-angle-down:before {\n  content: \"\\f107\";\n}\n.fa-desktop:before {\n  content: \"\\f108\";\n}\n.fa-laptop:before {\n  content: \"\\f109\";\n}\n.fa-tablet:before {\n  content: \"\\f10a\";\n}\n.fa-mobile-phone:before,\n.fa-mobile:before {\n  content: \"\\f10b\";\n}\n.fa-circle-o:before {\n  content: \"\\f10c\";\n}\n.fa-quote-left:before {\n  content: \"\\f10d\";\n}\n.fa-quote-right:before {\n  content: \"\\f10e\";\n}\n.fa-spinner:before {\n  content: \"\\f110\";\n}\n.fa-circle:before {\n  content: \"\\f111\";\n}\n.fa-mail-reply:before,\n.fa-reply:before {\n  content: \"\\f112\";\n}\n.fa-github-alt:before {\n  content: \"\\f113\";\n}\n.fa-folder-o:before {\n  content: \"\\f114\";\n}\n.fa-folder-open-o:before {\n  content: \"\\f115\";\n}\n.fa-smile-o:before {\n  content: \"\\f118\";\n}\n.fa-frown-o:before {\n  content: \"\\f119\";\n}\n.fa-meh-o:before {\n  content: \"\\f11a\";\n}\n.fa-gamepad:before {\n  content: \"\\f11b\";\n}\n.fa-keyboard-o:before {\n  content: \"\\f11c\";\n}\n.fa-flag-o:before {\n  content: \"\\f11d\";\n}\n.fa-flag-checkered:before {\n  content: \"\\f11e\";\n}\n.fa-terminal:before {\n  content: \"\\f120\";\n}\n.fa-code:before {\n  content: \"\\f121\";\n}\n.fa-mail-reply-all:before,\n.fa-reply-all:before {\n  content: \"\\f122\";\n}\n.fa-star-half-empty:before,\n.fa-star-half-full:before,\n.fa-star-half-o:before {\n  content: \"\\f123\";\n}\n.fa-location-arrow:before {\n  content: \"\\f124\";\n}\n.fa-crop:before {\n  content: \"\\f125\";\n}\n.fa-code-fork:before {\n  content: \"\\f126\";\n}\n.fa-unlink:before,\n.fa-chain-broken:before {\n  content: \"\\f127\";\n}\n.fa-question:before {\n  content: \"\\f128\";\n}\n.fa-info:before {\n  content: \"\\f129\";\n}\n.fa-exclamation:before {\n  content: \"\\f12a\";\n}\n.fa-superscript:before {\n  content: \"\\f12b\";\n}\n.fa-subscript:before {\n  content: \"\\f12c\";\n}\n.fa-eraser:before {\n  content: \"\\f12d\";\n}\n.fa-puzzle-piece:before {\n  content: \"\\f12e\";\n}\n.fa-microphone:before {\n  content: \"\\f130\";\n}\n.fa-microphone-slash:before {\n  content: \"\\f131\";\n}\n.fa-shield:before {\n  content: \"\\f132\";\n}\n.fa-calendar-o:before {\n  content: \"\\f133\";\n}\n.fa-fire-extinguisher:before {\n  content: \"\\f134\";\n}\n.fa-rocket:before {\n  content: \"\\f135\";\n}\n.fa-maxcdn:before {\n  content: \"\\f136\";\n}\n.fa-chevron-circle-left:before {\n  content: \"\\f137\";\n}\n.fa-chevron-circle-right:before {\n  content: \"\\f138\";\n}\n.fa-chevron-circle-up:before {\n  content: \"\\f139\";\n}\n.fa-chevron-circle-down:before {\n  content: \"\\f13a\";\n}\n.fa-html5:before {\n  content: \"\\f13b\";\n}\n.fa-css3:before {\n  content: \"\\f13c\";\n}\n.fa-anchor:before {\n  content: \"\\f13d\";\n}\n.fa-unlock-alt:before {\n  content: \"\\f13e\";\n}\n.fa-bullseye:before {\n  content: \"\\f140\";\n}\n.fa-ellipsis-h:before {\n  content: \"\\f141\";\n}\n.fa-ellipsis-v:before {\n  content: \"\\f142\";\n}\n.fa-rss-square:before {\n  content: \"\\f143\";\n}\n.fa-play-circle:before {\n  content: \"\\f144\";\n}\n.fa-ticket:before {\n  content: \"\\f145\";\n}\n.fa-minus-square:before {\n  content: \"\\f146\";\n}\n.fa-minus-square-o:before {\n  content: \"\\f147\";\n}\n.fa-level-up:before {\n  content: \"\\f148\";\n}\n.fa-level-down:before {\n  content: \"\\f149\";\n}\n.fa-check-square:before {\n  content: \"\\f14a\";\n}\n.fa-pencil-square:before {\n  content: \"\\f14b\";\n}\n.fa-external-link-square:before {\n  content: \"\\f14c\";\n}\n.fa-share-square:before {\n  content: \"\\f14d\";\n}\n.fa-compass:before {\n  content: \"\\f14e\";\n}\n.fa-toggle-down:before,\n.fa-caret-square-o-down:before {\n  content: \"\\f150\";\n}\n.fa-toggle-up:before,\n.fa-caret-square-o-up:before {\n  content: \"\\f151\";\n}\n.fa-toggle-right:before,\n.fa-caret-square-o-right:before {\n  content: \"\\f152\";\n}\n.fa-euro:before,\n.fa-eur:before {\n  content: \"\\f153\";\n}\n.fa-gbp:before {\n  content: \"\\f154\";\n}\n.fa-dollar:before,\n.fa-usd:before {\n  content: \"\\f155\";\n}\n.fa-rupee:before,\n.fa-inr:before {\n  content: \"\\f156\";\n}\n.fa-cny:before,\n.fa-rmb:before,\n.fa-yen:before,\n.fa-jpy:before {\n  content: \"\\f157\";\n}\n.fa-ruble:before,\n.fa-rouble:before,\n.fa-rub:before {\n  content: \"\\f158\";\n}\n.fa-won:before,\n.fa-krw:before {\n  content: \"\\f159\";\n}\n.fa-bitcoin:before,\n.fa-btc:before {\n  content: \"\\f15a\";\n}\n.fa-file:before {\n  content: \"\\f15b\";\n}\n.fa-file-text:before {\n  content: \"\\f15c\";\n}\n.fa-sort-alpha-asc:before {\n  content: \"\\f15d\";\n}\n.fa-sort-alpha-desc:before {\n  content: \"\\f15e\";\n}\n.fa-sort-amount-asc:before {\n  content: \"\\f160\";\n}\n.fa-sort-amount-desc:before {\n  content: \"\\f161\";\n}\n.fa-sort-numeric-asc:before {\n  content: \"\\f162\";\n}\n.fa-sort-numeric-desc:before {\n  content: \"\\f163\";\n}\n.fa-thumbs-up:before {\n  content: \"\\f164\";\n}\n.fa-thumbs-down:before {\n  content: \"\\f165\";\n}\n.fa-youtube-square:before {\n  content: \"\\f166\";\n}\n.fa-youtube:before {\n  content: \"\\f167\";\n}\n.fa-xing:before {\n  content: \"\\f168\";\n}\n.fa-xing-square:before {\n  content: \"\\f169\";\n}\n.fa-youtube-play:before {\n  content: \"\\f16a\";\n}\n.fa-dropbox:before {\n  content: \"\\f16b\";\n}\n.fa-stack-overflow:before {\n  content: \"\\f16c\";\n}\n.fa-instagram:before {\n  content: \"\\f16d\";\n}\n.fa-flickr:before {\n  content: \"\\f16e\";\n}\n.fa-adn:before {\n  content: \"\\f170\";\n}\n.fa-bitbucket:before {\n  content: \"\\f171\";\n}\n.fa-bitbucket-square:before {\n  content: \"\\f172\";\n}\n.fa-tumblr:before {\n  content: \"\\f173\";\n}\n.fa-tumblr-square:before {\n  content: \"\\f174\";\n}\n.fa-long-arrow-down:before {\n  content: \"\\f175\";\n}\n.fa-long-arrow-up:before {\n  content: \"\\f176\";\n}\n.fa-long-arrow-left:before {\n  content: \"\\f177\";\n}\n.fa-long-arrow-right:before {\n  content: \"\\f178\";\n}\n.fa-apple:before {\n  content: \"\\f179\";\n}\n.fa-windows:before {\n  content: \"\\f17a\";\n}\n.fa-android:before {\n  content: \"\\f17b\";\n}\n.fa-linux:before {\n  content: \"\\f17c\";\n}\n.fa-dribbble:before {\n  content: \"\\f17d\";\n}\n.fa-skype:before {\n  content: \"\\f17e\";\n}\n.fa-foursquare:before {\n  content: \"\\f180\";\n}\n.fa-trello:before {\n  content: \"\\f181\";\n}\n.fa-female:before {\n  content: \"\\f182\";\n}\n.fa-male:before {\n  content: \"\\f183\";\n}\n.fa-gittip:before,\n.fa-gratipay:before {\n  content: \"\\f184\";\n}\n.fa-sun-o:before {\n  content: \"\\f185\";\n}\n.fa-moon-o:before {\n  content: \"\\f186\";\n}\n.fa-archive:before {\n  content: \"\\f187\";\n}\n.fa-bug:before {\n  content: \"\\f188\";\n}\n.fa-vk:before {\n  content: \"\\f189\";\n}\n.fa-weibo:before {\n  content: \"\\f18a\";\n}\n.fa-renren:before {\n  content: \"\\f18b\";\n}\n.fa-pagelines:before {\n  content: \"\\f18c\";\n}\n.fa-stack-exchange:before {\n  content: \"\\f18d\";\n}\n.fa-arrow-circle-o-right:before {\n  content: \"\\f18e\";\n}\n.fa-arrow-circle-o-left:before {\n  content: \"\\f190\";\n}\n.fa-toggle-left:before,\n.fa-caret-square-o-left:before {\n  content: \"\\f191\";\n}\n.fa-dot-circle-o:before {\n  content: \"\\f192\";\n}\n.fa-wheelchair:before {\n  content: \"\\f193\";\n}\n.fa-vimeo-square:before {\n  content: \"\\f194\";\n}\n.fa-turkish-lira:before,\n.fa-try:before {\n  content: \"\\f195\";\n}\n.fa-plus-square-o:before {\n  content: \"\\f196\";\n}\n.fa-space-shuttle:before {\n  content: \"\\f197\";\n}\n.fa-slack:before {\n  content: \"\\f198\";\n}\n.fa-envelope-square:before {\n  content: \"\\f199\";\n}\n.fa-wordpress:before {\n  content: \"\\f19a\";\n}\n.fa-openid:before {\n  content: \"\\f19b\";\n}\n.fa-institution:before,\n.fa-bank:before,\n.fa-university:before {\n  content: \"\\f19c\";\n}\n.fa-mortar-board:before,\n.fa-graduation-cap:before {\n  content: \"\\f19d\";\n}\n.fa-yahoo:before {\n  content: \"\\f19e\";\n}\n.fa-google:before {\n  content: \"\\f1a0\";\n}\n.fa-reddit:before {\n  content: \"\\f1a1\";\n}\n.fa-reddit-square:before {\n  content: \"\\f1a2\";\n}\n.fa-stumbleupon-circle:before {\n  content: \"\\f1a3\";\n}\n.fa-stumbleupon:before {\n  content: \"\\f1a4\";\n}\n.fa-delicious:before {\n  content: \"\\f1a5\";\n}\n.fa-digg:before {\n  content: \"\\f1a6\";\n}\n.fa-pied-piper-pp:before {\n  content: \"\\f1a7\";\n}\n.fa-pied-piper-alt:before {\n  content: \"\\f1a8\";\n}\n.fa-drupal:before {\n  content: \"\\f1a9\";\n}\n.fa-joomla:before {\n  content: \"\\f1aa\";\n}\n.fa-language:before {\n  content: \"\\f1ab\";\n}\n.fa-fax:before {\n  content: \"\\f1ac\";\n}\n.fa-building:before {\n  content: \"\\f1ad\";\n}\n.fa-child:before {\n  content: \"\\f1ae\";\n}\n.fa-paw:before {\n  content: \"\\f1b0\";\n}\n.fa-spoon:before {\n  content: \"\\f1b1\";\n}\n.fa-cube:before {\n  content: \"\\f1b2\";\n}\n.fa-cubes:before {\n  content: \"\\f1b3\";\n}\n.fa-behance:before {\n  content: \"\\f1b4\";\n}\n.fa-behance-square:before {\n  content: \"\\f1b5\";\n}\n.fa-steam:before {\n  content: \"\\f1b6\";\n}\n.fa-steam-square:before {\n  content: \"\\f1b7\";\n}\n.fa-recycle:before {\n  content: \"\\f1b8\";\n}\n.fa-automobile:before,\n.fa-car:before {\n  content: \"\\f1b9\";\n}\n.fa-cab:before,\n.fa-taxi:before {\n  content: \"\\f1ba\";\n}\n.fa-tree:before {\n  content: \"\\f1bb\";\n}\n.fa-spotify:before {\n  content: \"\\f1bc\";\n}\n.fa-deviantart:before {\n  content: \"\\f1bd\";\n}\n.fa-soundcloud:before {\n  content: \"\\f1be\";\n}\n.fa-database:before {\n  content: \"\\f1c0\";\n}\n.fa-file-pdf-o:before {\n  content: \"\\f1c1\";\n}\n.fa-file-word-o:before {\n  content: \"\\f1c2\";\n}\n.fa-file-excel-o:before {\n  content: \"\\f1c3\";\n}\n.fa-file-powerpoint-o:before {\n  content: \"\\f1c4\";\n}\n.fa-file-photo-o:before,\n.fa-file-picture-o:before,\n.fa-file-image-o:before {\n  content: \"\\f1c5\";\n}\n.fa-file-zip-o:before,\n.fa-file-archive-o:before {\n  content: \"\\f1c6\";\n}\n.fa-file-sound-o:before,\n.fa-file-audio-o:before {\n  content: \"\\f1c7\";\n}\n.fa-file-movie-o:before,\n.fa-file-video-o:before {\n  content: \"\\f1c8\";\n}\n.fa-file-code-o:before {\n  content: \"\\f1c9\";\n}\n.fa-vine:before {\n  content: \"\\f1ca\";\n}\n.fa-codepen:before {\n  content: \"\\f1cb\";\n}\n.fa-jsfiddle:before {\n  content: \"\\f1cc\";\n}\n.fa-life-bouy:before,\n.fa-life-buoy:before,\n.fa-life-saver:before,\n.fa-support:before,\n.fa-life-ring:before {\n  content: \"\\f1cd\";\n}\n.fa-circle-o-notch:before {\n  content: \"\\f1ce\";\n}\n.fa-ra:before,\n.fa-resistance:before,\n.fa-rebel:before {\n  content: \"\\f1d0\";\n}\n.fa-ge:before,\n.fa-empire:before {\n  content: \"\\f1d1\";\n}\n.fa-git-square:before {\n  content: \"\\f1d2\";\n}\n.fa-git:before {\n  content: \"\\f1d3\";\n}\n.fa-y-combinator-square:before,\n.fa-yc-square:before,\n.fa-hacker-news:before {\n  content: \"\\f1d4\";\n}\n.fa-tencent-weibo:before {\n  content: \"\\f1d5\";\n}\n.fa-qq:before {\n  content: \"\\f1d6\";\n}\n.fa-wechat:before,\n.fa-weixin:before {\n  content: \"\\f1d7\";\n}\n.fa-send:before,\n.fa-paper-plane:before {\n  content: \"\\f1d8\";\n}\n.fa-send-o:before,\n.fa-paper-plane-o:before {\n  content: \"\\f1d9\";\n}\n.fa-history:before {\n  content: \"\\f1da\";\n}\n.fa-circle-thin:before {\n  content: \"\\f1db\";\n}\n.fa-header:before {\n  content: \"\\f1dc\";\n}\n.fa-paragraph:before {\n  content: \"\\f1dd\";\n}\n.fa-sliders:before {\n  content: \"\\f1de\";\n}\n.fa-share-alt:before {\n  content: \"\\f1e0\";\n}\n.fa-share-alt-square:before {\n  content: \"\\f1e1\";\n}\n.fa-bomb:before {\n  content: \"\\f1e2\";\n}\n.fa-soccer-ball-o:before,\n.fa-futbol-o:before {\n  content: \"\\f1e3\";\n}\n.fa-tty:before {\n  content: \"\\f1e4\";\n}\n.fa-binoculars:before {\n  content: \"\\f1e5\";\n}\n.fa-plug:before {\n  content: \"\\f1e6\";\n}\n.fa-slideshare:before {\n  content: \"\\f1e7\";\n}\n.fa-twitch:before {\n  content: \"\\f1e8\";\n}\n.fa-yelp:before {\n  content: \"\\f1e9\";\n}\n.fa-newspaper-o:before {\n  content: \"\\f1ea\";\n}\n.fa-wifi:before {\n  content: \"\\f1eb\";\n}\n.fa-calculator:before {\n  content: \"\\f1ec\";\n}\n.fa-paypal:before {\n  content: \"\\f1ed\";\n}\n.fa-google-wallet:before {\n  content: \"\\f1ee\";\n}\n.fa-cc-visa:before {\n  content: \"\\f1f0\";\n}\n.fa-cc-mastercard:before {\n  content: \"\\f1f1\";\n}\n.fa-cc-discover:before {\n  content: \"\\f1f2\";\n}\n.fa-cc-amex:before {\n  content: \"\\f1f3\";\n}\n.fa-cc-paypal:before {\n  content: \"\\f1f4\";\n}\n.fa-cc-stripe:before {\n  content: \"\\f1f5\";\n}\n.fa-bell-slash:before {\n  content: \"\\f1f6\";\n}\n.fa-bell-slash-o:before {\n  content: \"\\f1f7\";\n}\n.fa-trash:before {\n  content: \"\\f1f8\";\n}\n.fa-copyright:before {\n  content: \"\\f1f9\";\n}\n.fa-at:before {\n  content: \"\\f1fa\";\n}\n.fa-eyedropper:before {\n  content: \"\\f1fb\";\n}\n.fa-paint-brush:before {\n  content: \"\\f1fc\";\n}\n.fa-birthday-cake:before {\n  content: \"\\f1fd\";\n}\n.fa-area-chart:before {\n  content: \"\\f1fe\";\n}\n.fa-pie-chart:before {\n  content: \"\\f200\";\n}\n.fa-line-chart:before {\n  content: \"\\f201\";\n}\n.fa-lastfm:before {\n  content: \"\\f202\";\n}\n.fa-lastfm-square:before {\n  content: \"\\f203\";\n}\n.fa-toggle-off:before {\n  content: \"\\f204\";\n}\n.fa-toggle-on:before {\n  content: \"\\f205\";\n}\n.fa-bicycle:before {\n  content: \"\\f206\";\n}\n.fa-bus:before {\n  content: \"\\f207\";\n}\n.fa-ioxhost:before {\n  content: \"\\f208\";\n}\n.fa-angellist:before {\n  content: \"\\f209\";\n}\n.fa-cc:before {\n  content: \"\\f20a\";\n}\n.fa-shekel:before,\n.fa-sheqel:before,\n.fa-ils:before {\n  content: \"\\f20b\";\n}\n.fa-meanpath:before {\n  content: \"\\f20c\";\n}\n.fa-buysellads:before {\n  content: \"\\f20d\";\n}\n.fa-connectdevelop:before {\n  content: \"\\f20e\";\n}\n.fa-dashcube:before {\n  content: \"\\f210\";\n}\n.fa-forumbee:before {\n  content: \"\\f211\";\n}\n.fa-leanpub:before {\n  content: \"\\f212\";\n}\n.fa-sellsy:before {\n  content: \"\\f213\";\n}\n.fa-shirtsinbulk:before {\n  content: \"\\f214\";\n}\n.fa-simplybuilt:before {\n  content: \"\\f215\";\n}\n.fa-skyatlas:before {\n  content: \"\\f216\";\n}\n.fa-cart-plus:before {\n  content: \"\\f217\";\n}\n.fa-cart-arrow-down:before {\n  content: \"\\f218\";\n}\n.fa-diamond:before {\n  content: \"\\f219\";\n}\n.fa-ship:before {\n  content: \"\\f21a\";\n}\n.fa-user-secret:before {\n  content: \"\\f21b\";\n}\n.fa-motorcycle:before {\n  content: \"\\f21c\";\n}\n.fa-street-view:before {\n  content: \"\\f21d\";\n}\n.fa-heartbeat:before {\n  content: \"\\f21e\";\n}\n.fa-venus:before {\n  content: \"\\f221\";\n}\n.fa-mars:before {\n  content: \"\\f222\";\n}\n.fa-mercury:before {\n  content: \"\\f223\";\n}\n.fa-intersex:before,\n.fa-transgender:before {\n  content: \"\\f224\";\n}\n.fa-transgender-alt:before {\n  content: \"\\f225\";\n}\n.fa-venus-double:before {\n  content: \"\\f226\";\n}\n.fa-mars-double:before {\n  content: \"\\f227\";\n}\n.fa-venus-mars:before {\n  content: \"\\f228\";\n}\n.fa-mars-stroke:before {\n  content: \"\\f229\";\n}\n.fa-mars-stroke-v:before {\n  content: \"\\f22a\";\n}\n.fa-mars-stroke-h:before {\n  content: \"\\f22b\";\n}\n.fa-neuter:before {\n  content: \"\\f22c\";\n}\n.fa-genderless:before {\n  content: \"\\f22d\";\n}\n.fa-facebook-official:before {\n  content: \"\\f230\";\n}\n.fa-pinterest-p:before {\n  content: \"\\f231\";\n}\n.fa-whatsapp:before {\n  content: \"\\f232\";\n}\n.fa-server:before {\n  content: \"\\f233\";\n}\n.fa-user-plus:before {\n  content: \"\\f234\";\n}\n.fa-user-times:before {\n  content: \"\\f235\";\n}\n.fa-hotel:before,\n.fa-bed:before {\n  content: \"\\f236\";\n}\n.fa-viacoin:before {\n  content: \"\\f237\";\n}\n.fa-train:before {\n  content: \"\\f238\";\n}\n.fa-subway:before {\n  content: \"\\f239\";\n}\n.fa-medium:before {\n  content: \"\\f23a\";\n}\n.fa-yc:before,\n.fa-y-combinator:before {\n  content: \"\\f23b\";\n}\n.fa-optin-monster:before {\n  content: \"\\f23c\";\n}\n.fa-opencart:before {\n  content: \"\\f23d\";\n}\n.fa-expeditedssl:before {\n  content: \"\\f23e\";\n}\n.fa-battery-4:before,\n.fa-battery:before,\n.fa-battery-full:before {\n  content: \"\\f240\";\n}\n.fa-battery-3:before,\n.fa-battery-three-quarters:before {\n  content: \"\\f241\";\n}\n.fa-battery-2:before,\n.fa-battery-half:before {\n  content: \"\\f242\";\n}\n.fa-battery-1:before,\n.fa-battery-quarter:before {\n  content: \"\\f243\";\n}\n.fa-battery-0:before,\n.fa-battery-empty:before {\n  content: \"\\f244\";\n}\n.fa-mouse-pointer:before {\n  content: \"\\f245\";\n}\n.fa-i-cursor:before {\n  content: \"\\f246\";\n}\n.fa-object-group:before {\n  content: \"\\f247\";\n}\n.fa-object-ungroup:before {\n  content: \"\\f248\";\n}\n.fa-sticky-note:before {\n  content: \"\\f249\";\n}\n.fa-sticky-note-o:before {\n  content: \"\\f24a\";\n}\n.fa-cc-jcb:before {\n  content: \"\\f24b\";\n}\n.fa-cc-diners-club:before {\n  content: \"\\f24c\";\n}\n.fa-clone:before {\n  content: \"\\f24d\";\n}\n.fa-balance-scale:before {\n  content: \"\\f24e\";\n}\n.fa-hourglass-o:before {\n  content: \"\\f250\";\n}\n.fa-hourglass-1:before,\n.fa-hourglass-start:before {\n  content: \"\\f251\";\n}\n.fa-hourglass-2:before,\n.fa-hourglass-half:before {\n  content: \"\\f252\";\n}\n.fa-hourglass-3:before,\n.fa-hourglass-end:before {\n  content: \"\\f253\";\n}\n.fa-hourglass:before {\n  content: \"\\f254\";\n}\n.fa-hand-grab-o:before,\n.fa-hand-rock-o:before {\n  content: \"\\f255\";\n}\n.fa-hand-stop-o:before,\n.fa-hand-paper-o:before {\n  content: \"\\f256\";\n}\n.fa-hand-scissors-o:before {\n  content: \"\\f257\";\n}\n.fa-hand-lizard-o:before {\n  content: \"\\f258\";\n}\n.fa-hand-spock-o:before {\n  content: \"\\f259\";\n}\n.fa-hand-pointer-o:before {\n  content: \"\\f25a\";\n}\n.fa-hand-peace-o:before {\n  content: \"\\f25b\";\n}\n.fa-trademark:before {\n  content: \"\\f25c\";\n}\n.fa-registered:before {\n  content: \"\\f25d\";\n}\n.fa-creative-commons:before {\n  content: \"\\f25e\";\n}\n.fa-gg:before {\n  content: \"\\f260\";\n}\n.fa-gg-circle:before {\n  content: \"\\f261\";\n}\n.fa-tripadvisor:before {\n  content: \"\\f262\";\n}\n.fa-odnoklassniki:before {\n  content: \"\\f263\";\n}\n.fa-odnoklassniki-square:before {\n  content: \"\\f264\";\n}\n.fa-get-pocket:before {\n  content: \"\\f265\";\n}\n.fa-wikipedia-w:before {\n  content: \"\\f266\";\n}\n.fa-safari:before {\n  content: \"\\f267\";\n}\n.fa-chrome:before {\n  content: \"\\f268\";\n}\n.fa-firefox:before {\n  content: \"\\f269\";\n}\n.fa-opera:before {\n  content: \"\\f26a\";\n}\n.fa-internet-explorer:before {\n  content: \"\\f26b\";\n}\n.fa-tv:before,\n.fa-television:before {\n  content: \"\\f26c\";\n}\n.fa-contao:before {\n  content: \"\\f26d\";\n}\n.fa-500px:before {\n  content: \"\\f26e\";\n}\n.fa-amazon:before {\n  content: \"\\f270\";\n}\n.fa-calendar-plus-o:before {\n  content: \"\\f271\";\n}\n.fa-calendar-minus-o:before {\n  content: \"\\f272\";\n}\n.fa-calendar-times-o:before {\n  content: \"\\f273\";\n}\n.fa-calendar-check-o:before {\n  content: \"\\f274\";\n}\n.fa-industry:before {\n  content: \"\\f275\";\n}\n.fa-map-pin:before {\n  content: \"\\f276\";\n}\n.fa-map-signs:before {\n  content: \"\\f277\";\n}\n.fa-map-o:before {\n  content: \"\\f278\";\n}\n.fa-map:before {\n  content: \"\\f279\";\n}\n.fa-commenting:before {\n  content: \"\\f27a\";\n}\n.fa-commenting-o:before {\n  content: \"\\f27b\";\n}\n.fa-houzz:before {\n  content: \"\\f27c\";\n}\n.fa-vimeo:before {\n  content: \"\\f27d\";\n}\n.fa-black-tie:before {\n  content: \"\\f27e\";\n}\n.fa-fonticons:before {\n  content: \"\\f280\";\n}\n.fa-reddit-alien:before {\n  content: \"\\f281\";\n}\n.fa-edge:before {\n  content: \"\\f282\";\n}\n.fa-credit-card-alt:before {\n  content: \"\\f283\";\n}\n.fa-codiepie:before {\n  content: \"\\f284\";\n}\n.fa-modx:before {\n  content: \"\\f285\";\n}\n.fa-fort-awesome:before {\n  content: \"\\f286\";\n}\n.fa-usb:before {\n  content: \"\\f287\";\n}\n.fa-product-hunt:before {\n  content: \"\\f288\";\n}\n.fa-mixcloud:before {\n  content: \"\\f289\";\n}\n.fa-scribd:before {\n  content: \"\\f28a\";\n}\n.fa-pause-circle:before {\n  content: \"\\f28b\";\n}\n.fa-pause-circle-o:before {\n  content: \"\\f28c\";\n}\n.fa-stop-circle:before {\n  content: \"\\f28d\";\n}\n.fa-stop-circle-o:before {\n  content: \"\\f28e\";\n}\n.fa-shopping-bag:before {\n  content: \"\\f290\";\n}\n.fa-shopping-basket:before {\n  content: \"\\f291\";\n}\n.fa-hashtag:before {\n  content: \"\\f292\";\n}\n.fa-bluetooth:before {\n  content: \"\\f293\";\n}\n.fa-bluetooth-b:before {\n  content: \"\\f294\";\n}\n.fa-percent:before {\n  content: \"\\f295\";\n}\n.fa-gitlab:before {\n  content: \"\\f296\";\n}\n.fa-wpbeginner:before {\n  content: \"\\f297\";\n}\n.fa-wpforms:before {\n  content: \"\\f298\";\n}\n.fa-envira:before {\n  content: \"\\f299\";\n}\n.fa-universal-access:before {\n  content: \"\\f29a\";\n}\n.fa-wheelchair-alt:before {\n  content: \"\\f29b\";\n}\n.fa-question-circle-o:before {\n  content: \"\\f29c\";\n}\n.fa-blind:before {\n  content: \"\\f29d\";\n}\n.fa-audio-description:before {\n  content: \"\\f29e\";\n}\n.fa-volume-control-phone:before {\n  content: \"\\f2a0\";\n}\n.fa-braille:before {\n  content: \"\\f2a1\";\n}\n.fa-assistive-listening-systems:before {\n  content: \"\\f2a2\";\n}\n.fa-asl-interpreting:before,\n.fa-american-sign-language-interpreting:before {\n  content: \"\\f2a3\";\n}\n.fa-deafness:before,\n.fa-hard-of-hearing:before,\n.fa-deaf:before {\n  content: \"\\f2a4\";\n}\n.fa-glide:before {\n  content: \"\\f2a5\";\n}\n.fa-glide-g:before {\n  content: \"\\f2a6\";\n}\n.fa-signing:before,\n.fa-sign-language:before {\n  content: \"\\f2a7\";\n}\n.fa-low-vision:before {\n  content: \"\\f2a8\";\n}\n.fa-viadeo:before {\n  content: \"\\f2a9\";\n}\n.fa-viadeo-square:before {\n  content: \"\\f2aa\";\n}\n.fa-snapchat:before {\n  content: \"\\f2ab\";\n}\n.fa-snapchat-ghost:before {\n  content: \"\\f2ac\";\n}\n.fa-snapchat-square:before {\n  content: \"\\f2ad\";\n}\n.fa-pied-piper:before {\n  content: \"\\f2ae\";\n}\n.fa-first-order:before {\n  content: \"\\f2b0\";\n}\n.fa-yoast:before {\n  content: \"\\f2b1\";\n}\n.fa-themeisle:before {\n  content: \"\\f2b2\";\n}\n.fa-google-plus-circle:before,\n.fa-google-plus-official:before {\n  content: \"\\f2b3\";\n}\n.fa-fa:before,\n.fa-font-awesome:before {\n  content: \"\\f2b4\";\n}\n.fa-handshake-o:before {\n  content: \"\\f2b5\";\n}\n.fa-envelope-open:before {\n  content: \"\\f2b6\";\n}\n.fa-envelope-open-o:before {\n  content: \"\\f2b7\";\n}\n.fa-linode:before {\n  content: \"\\f2b8\";\n}\n.fa-address-book:before {\n  content: \"\\f2b9\";\n}\n.fa-address-book-o:before {\n  content: \"\\f2ba\";\n}\n.fa-vcard:before,\n.fa-address-card:before {\n  content: \"\\f2bb\";\n}\n.fa-vcard-o:before,\n.fa-address-card-o:before {\n  content: \"\\f2bc\";\n}\n.fa-user-circle:before {\n  content: \"\\f2bd\";\n}\n.fa-user-circle-o:before {\n  content: \"\\f2be\";\n}\n.fa-user-o:before {\n  content: \"\\f2c0\";\n}\n.fa-id-badge:before {\n  content: \"\\f2c1\";\n}\n.fa-drivers-license:before,\n.fa-id-card:before {\n  content: \"\\f2c2\";\n}\n.fa-drivers-license-o:before,\n.fa-id-card-o:before {\n  content: \"\\f2c3\";\n}\n.fa-quora:before {\n  content: \"\\f2c4\";\n}\n.fa-free-code-camp:before {\n  content: \"\\f2c5\";\n}\n.fa-telegram:before {\n  content: \"\\f2c6\";\n}\n.fa-thermometer-4:before,\n.fa-thermometer:before,\n.fa-thermometer-full:before {\n  content: \"\\f2c7\";\n}\n.fa-thermometer-3:before,\n.fa-thermometer-three-quarters:before {\n  content: \"\\f2c8\";\n}\n.fa-thermometer-2:before,\n.fa-thermometer-half:before {\n  content: \"\\f2c9\";\n}\n.fa-thermometer-1:before,\n.fa-thermometer-quarter:before {\n  content: \"\\f2ca\";\n}\n.fa-thermometer-0:before,\n.fa-thermometer-empty:before {\n  content: \"\\f2cb\";\n}\n.fa-shower:before {\n  content: \"\\f2cc\";\n}\n.fa-bathtub:before,\n.fa-s15:before,\n.fa-bath:before {\n  content: \"\\f2cd\";\n}\n.fa-podcast:before {\n  content: \"\\f2ce\";\n}\n.fa-window-maximize:before {\n  content: \"\\f2d0\";\n}\n.fa-window-minimize:before {\n  content: \"\\f2d1\";\n}\n.fa-window-restore:before {\n  content: \"\\f2d2\";\n}\n.fa-times-rectangle:before,\n.fa-window-close:before {\n  content: \"\\f2d3\";\n}\n.fa-times-rectangle-o:before,\n.fa-window-close-o:before {\n  content: \"\\f2d4\";\n}\n.fa-bandcamp:before {\n  content: \"\\f2d5\";\n}\n.fa-grav:before {\n  content: \"\\f2d6\";\n}\n.fa-etsy:before {\n  content: \"\\f2d7\";\n}\n.fa-imdb:before {\n  content: \"\\f2d8\";\n}\n.fa-ravelry:before {\n  content: \"\\f2d9\";\n}\n.fa-eercast:before {\n  content: \"\\f2da\";\n}\n.fa-microchip:before {\n  content: \"\\f2db\";\n}\n.fa-snowflake-o:before {\n  content: \"\\f2dc\";\n}\n.fa-superpowers:before {\n  content: \"\\f2dd\";\n}\n.fa-wpexplorer:before {\n  content: \"\\f2de\";\n}\n.fa-meetup:before {\n  content: \"\\f2e0\";\n}\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n  position: static;\n  width: auto;\n  height: auto;\n  margin: 0;\n  overflow: visible;\n  clip: auto;\n}\n"
  },
  {
    "path": "src/components/Backtop/BackTop.vue",
    "content": "<template>\n  <el-backtop :bottom=\"60\" :right=\"60\">\n    <div title=\"回到顶部\"\n        style=\"{\n        height: 100%;\n        width: 100%;\n        background-color: #f2f5f6;\n        box-shadow: 0 1px 0 0;\n        border-radius: 12px;\n        text-align: center;\n        line-height: 40px;\n        color: #167df0;\n      }\"\n    >\n      <i class=\"fa fa-arrow-up\"></i>\n    </div>\n  </el-backtop>\n</template>\n\n<script>\nexport default {\n  name: \"BackTop\"\n}\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "src/components/Comment/Comments.vue",
    "content": "<template>\n  <section class=\"box comments\">\n    <hr />\n    <h3 class=\"title is-5\">Comments</h3>\n    <lv-comments-form :slug=\"slug\" v-if=\"token\" @loadComments=\"fetchComments\"/>\n\n    <lv-comments-item\n      v-for=\"comment in comments\"\n      :key=\"`comment-${comment.id}`\"\n      :comment=\"comment\"\n    />\n  </section>\n</template>\n\n<script>\nimport { mapGetters } from 'vuex'\nimport { fetchCommentsByTopicId } from '@/api/comment'\nimport LvCommentsForm from './CommentsForm'\nimport LvCommentsItem from './CommentsItem'\n\nexport default {\n  name: 'LvComments',\n  components: {\n    LvCommentsForm,\n    LvCommentsItem\n  },\n  data() {\n    return {\n      comments: []\n    }\n  },\n  props: {\n    slug: {\n      type: String,\n      default: null\n    }\n  },\n  computed: {\n    ...mapGetters([\n      'token'\n    ])\n  },\n  async mounted() {\n    await this.fetchComments(this.slug)\n  },\n  methods: {\n    // 初始化\n    async fetchComments(topic_id) {\n      console.log(topic_id)\n      fetchCommentsByTopicId(topic_id).then(response => {\n        const { data } = response\n        this.comments = data\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/Comment/CommentsForm.vue",
    "content": "<template>\n  <article class=\"media\">\n    <div class=\"media-content\">\n      <form @submit.prevent=\"onSubmit\">\n        <b-field>\n          <b-input\n            v-model.lazy=\"commentText\"\n            type=\"textarea\"\n            maxlength=\"400\"\n            placeholder=\"Add a comment...\"\n            :disabled=\"isLoading\"\n          ></b-input>\n        </b-field>\n        <nav class=\"level\">\n          <div class=\"level-left\">\n            <b-button\n              type=\"is-primary\"\n              native-type=\"submit\"\n              class=\"level-item\"\n              :disabled=\"isLoading\"\n            >\n              Comment\n            </b-button>\n          </div>\n        </nav>\n      </form>\n    </div>\n  </article>\n</template>\n\n<script>\nimport { pushComment } from '@/api/comment'\n\nexport default {\n  name: 'LvCommentsForm',\n  data() {\n    return {\n      commentText: '',\n      isLoading: false\n    }\n  },\n  props: {\n    slug: {\n      type: String,\n      default: null\n    }\n  },\n  methods: {\n    async onSubmit() {\n      this.isLoading = true\n      try {\n        let postData = {}\n        console.log(this.commentText)\n        postData['content'] = this.commentText\n        postData['topic_id'] = this.slug\n        await pushComment(postData)\n        this.$emit('loadComments', this.slug)\n        this.$message.success('留言成功')\n      } catch (e) {\n        this.$buefy.toast.open({\n          message: `Cannot comment this story. ${e}`,\n          type: 'is-danger'\n        })\n      } finally {\n        this.isLoading = false\n      }\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/Comment/CommentsItem.vue",
    "content": "<template>\n  <article class=\"media\">\n    <figure class=\"media-left image is-48x48\">\n      <img :src=\"`https://cn.gravatar.com/avatar/${comment.userId}?s=164&d=monsterid`\">\n    </figure>\n    <div class=\"media-content\">\n      <div class=\"content\">\n        <p>\n          <strong>{{ comment.username }}</strong>\n          <small class=\"ml-2\">{{ comment.createTime | date }}</small>\n          <br />\n          {{ comment.content }}\n        </p>\n      </div>\n    </div>\n  </article>\n</template>\n\n<script>\n\nexport default {\n  name: 'LvCommentsItem',\n  props: {\n    comment: {\n      type: Object,\n      required: true\n    }\n  }\n}\n</script>\n\n\n"
  },
  {
    "path": "src/components/Layout/Footer.vue",
    "content": "<template>\n  <footer class=\"footer has-text-grey-light has-background-grey-darker\">\n    <div class=\"container\">\n      <div class=\"\">\n        <span>简洁、实用、美观</span>\n\n        <span style=\"float: right\">\n          <router-link :to=\"{path:'/admin/login'}\">\n            管理员登录\n          </router-link>\n          |\n          <a href=\"/?lang=zh_CN\">中文</a> |\n          <a href=\"/?lang=en_US\">English</a>\n        </span>\n      </div>\n\n      <div>\n        <span>{{ title }} ALL RIGHTS RESERVED</span>\n        <div style=\"float: right\">\n          <template>\n            <b-taglist attached>\n              <b-tag type=\"is-dark\" size=\"is-normal\">Design</b-tag>\n              <b-tag type=\"is-info\" size=\"is-normal\">{{ author }}</b-tag>\n            </b-taglist>\n          </template>\n        </div>\n      </div>\n    </div>\n    <back-top></back-top>\n  </footer>\n</template>\n\n<script>\nimport BackTop from \"@/components/Backtop/BackTop\";\n\nexport default {\n  name: \"Footer\",\n  components: {\n    BackTop\n  },\n  data() {\n    return {\n      title: \"© \" + new Date().getFullYear() + ' 豆约翰',\n      author: '豆约翰',\n    };\n  },\n};\n</script>\n\n<style scoped>\n\nfooter {\n  margin-top: 120px;\n  height: 150px;\n}\nfooter a{\n  color: #bfbfbf;\n}\n\n</style>"
  },
  {
    "path": "src/components/Layout/Header.vue",
    "content": "<template>\n  <header class=\"header has-background-white has-text-black\">\n    <b-navbar\n      class=\"container is-white\"\n      :fixed-top=\"true\"\n    >\n      <template slot=\"brand\">\n        <b-navbar-item tag=\"div\">\n          <img :src=\"doubaoImg\" alt=\"logo\">\n        </b-navbar-item>\n\n        <b-navbar-item\n          class=\"is-hidden-desktop\"\n          tag=\"router-link\"\n          :to=\"{ path: '/' }\"\n        >\n          主页\n        </b-navbar-item>\n      </template>\n      <template slot=\"start\">\n        <b-navbar-item\n          tag=\"router-link\"\n          :to=\"{ path: '/' }\"\n        >\n          🌐 主页\n        </b-navbar-item>\n      </template>\n\n      <template slot=\"end\">\n        <b-navbar-item tag=\"div\">\n          <b-field position=\"is-centered\">\n            <b-input\n              v-model=\"searchKey\"\n              class=\"s_input\"\n              width=\"80%\"\n              placeholder=\"搜索帖子、标签和用户\"\n              rounded\n              clearable\n              @keyup.enter.native=\"search()\"\n            />\n\n            <p class=\"control\">\n              <b-button\n                class=\"is-info\"\n                @click=\"search()\"\n              >检索\n              </b-button>\n            </p>\n          </b-field>\n        </b-navbar-item>\n\n        <b-navbar-item tag=\"div\">\n          <b-switch\n            v-model=\"darkMode\"\n            passive-type=\"is-warning\"\n            type=\"is-dark\"\n          >\n            {{ darkMode ? \"夜\" : \"日\" }}\n          </b-switch>\n        </b-navbar-item>\n\n        <b-navbar-item\n          v-if=\"token == null || token === ''\"\n          tag=\"div\"\n        >\n          <div class=\"buttons\">\n            <b-button\n              class=\"is-light\"\n              tag=\"router-link\"\n              :to=\"{ path: '/register' }\"\n            >\n              注册\n            </b-button>\n            <b-button\n              class=\"is-light\"\n              tag=\"router-link\"\n              :to=\"{ path: '/login' }\"\n            >\n              登录\n            </b-button>\n          </div>\n        </b-navbar-item>\n\n        <b-navbar-dropdown\n          v-else\n          :label=\"user.alias\"\n        >\n          <b-navbar-item\n            tag=\"router-link\"\n            :to=\"{ path: `/member/${user.username}/home` }\"\n          >\n            🧘 个人中心\n          </b-navbar-item>\n          <hr class=\"dropdown-divider\">\n          <b-navbar-item\n            tag=\"router-link\"\n            :to=\"{ path: `/member/${user.username}/setting` }\"\n          >\n            ⚙ 设置中心\n          </b-navbar-item>\n          <hr class=\"dropdown-divider\">\n          <b-navbar-item\n            tag=\"a\"\n            @click=\"logout\"\n          > 👋 退出登录\n          </b-navbar-item>\n        </b-navbar-dropdown>\n      </template>\n    </b-navbar>\n  </header>\n</template>\n\n<script>\nimport { disable as disableDarkMode, enable as enableDarkMode } from 'darkreader'\nimport { getDarkMode, setDarkMode } from '@/utils/auth'\nimport { mapGetters } from 'vuex'\n\nexport default {\n  name: 'Header',\n  data() {\n    return {\n      logoUrl: require('@/assets/logo.png'),\n      doubaoImg: require('@/assets/image/doubao.png'),\n      searchKey: '',\n      darkMode: false\n    }\n  },\n  computed: {\n    ...mapGetters(['token', 'user'])\n  },\n  watch: {\n    // 监听Theme模式\n    darkMode(val) {\n      if (val) {\n        enableDarkMode({})\n      } else {\n        disableDarkMode()\n      }\n      setDarkMode(this.darkMode)\n    }\n  },\n  created() {\n    // 获取cookie中的夜间还是白天模式\n    this.darkMode = getDarkMode()\n    if (this.darkMode) {\n      enableDarkMode({})\n    } else {\n      disableDarkMode()\n    }\n  },\n  methods: {\n    async logout() {\n      this.$store.dispatch('user/logout').then(() => {\n        this.$message.info('退出登录成功')\n        setTimeout(() => {\n          this.$router.push({ path: this.redirect || '/' })\n        }, 500)\n      })\n    },\n    search() {\n      console.log(this.token)\n      if (this.searchKey.trim() === null || this.searchKey.trim() === '') {\n        this.$message.info({\n          showClose: true,\n          message: '请输入关键字搜索！',\n          type: 'warning'\n        })\n        return false\n      }\n      this.$router.push({ path: '/search?key=' + this.searchKey })\n    }\n  }\n}\n</script>\n\n<style scoped>\ninput {\n  width: 80%;\n  height: 86%;\n}\n</style>\n"
  },
  {
    "path": "src/components/Pagination/index.vue",
    "content": "<template>\n  <div :class=\"{ hidden: hidden }\" class=\"pagination-container\">\n    <el-pagination\n      :background=\"background\"\n      :current-page.sync=\"currentPage\"\n      :page-size.sync=\"pageSize\"\n      :layout=\"layout\"\n      :page-sizes=\"pageSizes\"\n      :total=\"total\"\n      v-bind=\"$attrs\"\n      @size-change=\"handleSizeChange\"\n      @current-change=\"handleCurrentChange\"\n    />\n  </div>\n</template>\n\n<script>\nimport {scrollTo} from \"@/utils/scroll-to\";\n\nexport default {\n  name: \"Pagination\",\n  props: {\n    total: {\n      required: true,\n      type: Number,\n    },\n    page: {\n      type: Number,\n      default: 1,\n    },\n    limit: {\n      type: Number,\n      default: 10,\n    },\n    pageSizes: {\n      type: Array,\n      default() {\n        return [5, 10, 20, 30, 50];\n      },\n    },\n    layout: {\n      type: String,\n      default: \"total, sizes, prev, pager, next, jumper\",\n      // default: 'sizes, prev, pager, next, jumper'\n    },\n    background: {\n      type: Boolean,\n      default: true,\n    },\n    autoScroll: {\n      type: Boolean,\n      default: true,\n    },\n    hidden: {\n      type: Boolean,\n      default: false,\n    },\n  },\n  computed: {\n    currentPage: {\n      get() {\n        return this.page;\n      },\n      set(val) {\n        this.$emit(\"update:page\", val);\n      },\n    },\n    pageSize: {\n      get() {\n        return this.limit;\n      },\n      set(val) {\n        this.$emit(\"update:limit\", val);\n      },\n    },\n  },\n  methods: {\n    handleSizeChange(val) {\n      this.$emit(\"pagination\", { page: this.currentPage, limit: val });\n      if (this.autoScroll) {\n        scrollTo(0, 800);\n      }\n    },\n    handleCurrentChange(val) {\n      this.$emit(\"pagination\", { page: val, limit: this.pageSize });\n      if (this.autoScroll) {\n        scrollTo(0, 800);\n      }\n    },\n  },\n};\n</script>\n\n<style scoped>\n.pagination-container {\n  /* background: #fff; */\n  padding: 5px 0px;\n}\n\n.pagination-container.hidden {\n  display: none;\n}\n</style>\n"
  },
  {
    "path": "src/main.js",
    "content": "import Vue from 'vue'\nimport App from './App.vue'\nimport router from './router'\nimport store from './store'\n// Buefy\nimport Buefy from 'buefy'\nimport 'buefy/dist/buefy.css'\n// ElementUI\nimport ElementUI from 'element-ui';\nimport 'element-ui/lib/theme-chalk/index.css';\nimport '@/assets/app.css'\nimport './assets/plugins/font-awesome-4.7.0/css/font-awesome.min.css'\nimport format from 'date-fns/format'\nimport '@/permission'\nimport relativeTime from 'dayjs/plugin/relativeTime';\n\n// 国际化\nimport 'dayjs/locale/zh-cn'\nconst dayjs = require('dayjs');\n\n// 相对时间插件\ndayjs.extend(relativeTime)\n\ndayjs.locale('zh-cn') // use locale globally\ndayjs().locale('zh-cn').format() // use locale in a specific instance\n\nVue.prototype.dayjs = dayjs;//可以全局使用dayjs\n\nVue.filter('date', (date) => {\n  return format(new Date(date), 'yyyy-MM-dd')\n})\n\nVue.use(Buefy)\nVue.use(ElementUI);\n\nVue.config.productionTip = false\n\nnew Vue({\n  router,\n  store,\n  render: h => h(App)\n}).$mount('#app')\n"
  },
  {
    "path": "src/permission.js",
    "content": "import router from './router'\nimport store from './store'\nimport getPageTitle from '@/utils/get-page-title'\n\nimport NProgress from 'nprogress' // progress bar\nimport 'nprogress/nprogress.css'\nimport {getToken} from \"@/utils/auth\"; // progress bar style\n\nNProgress.configure({showSpinner: false}) // NProgress Configuration\n\nrouter.beforeEach(async (to, from, next) => {\n    // start progress bar\n    NProgress.start()\n    // set page title\n    document.title = getPageTitle(to.meta.title)\n    // determine whether the user has logged in\n    const hasToken = getToken();\n\n    if (hasToken) {\n        if (to.path === '/login') {\n            // 登录，跳转首页\n            next({path: '/'})\n            NProgress.done()\n        } else {\n            // 获取用户信息\n            await store.dispatch('user/getInfo')\n            next()\n        }\n    } else if (!to.meta.requireAuth)\n    {\n        next()\n    }\n    else {\n        next('/login')\n    }\n})\n\nrouter.afterEach(() => {\n    // finish progress bar\n    NProgress.done()\n})\n"
  },
  {
    "path": "src/router/index.js",
    "content": "import Vue from \"vue\";\nimport VueRouter from \"vue-router\";\n\nVue.use(VueRouter);\n\nconst routes = [\n  {\n    path: \"/\",\n    name: \"Home\",\n    component: () => import(\"@/views/Home\"),\n  },\n  {\n    path: \"/register\",\n    name: \"register\",\n    component: () => import(\"@/views/auth/Register\"),\n    meta: { title: \"注册\" },\n  },\n  // 登录\n  {\n    name: \"login\",\n    path: \"/login\",\n    component: () => import(\"@/views/auth/Login\"),\n    meta: { title: \"登录\" },\n  },\n  // 发布\n  {\n    name: \"post-create\",\n    path: \"/post/create\",\n    component: () => import(\"@/views/post/Create\"),\n    meta: { title: \"信息发布\", requireAuth: true },\n  },\n  // 编辑\n  {\n    name: 'topic-edit',\n    path: '/topic/edit/:id',\n    component: () => import('@/views/post/Edit'),\n    meta: {\n      title: '编辑',\n      requireAuth: true\n    }\n  },\n  // 详情\n  {\n    name: \"post-detail\",\n    path: \"/post/:id\",\n    component: () => import(\"@/views/post/Detail\"),\n    meta: { title: \"详情\" },\n  },\n  {\n    name: 'tag',\n    path: '/tag/:name',\n    component: () => import('@/views/tag/Tag'),\n    meta: { title: '主题列表' }\n  },\n  // 检索\n  {\n    name: 'search',\n    path: '/search',\n    component: () => import('@/views/Search'),\n    meta: { title: '检索' }\n  },\n  // 用户主页\n  {\n    name: 'user',\n    path: '/member/:username/home',\n    component: () => import('@/views/user/Profile'),\n    meta: { title: '用户主页' }\n  },\n  // 用户设置\n  {\n    name: 'user-setting',\n    path: '/member/:username/setting',\n    component: () => import('@/views/user/Setting'),\n    meta: { title: '设置', requireAuth: true }\n  },\n  {\n    path: \"/404\",\n    name: \"404\",\n    component: () => import(\"@/views/error/404\"),\n    meta: { title: \"404-NotFound\" },\n  },\n  {\n    path: \"*\",\n    redirect: \"/404\",\n    hidden: true,\n  },\n];\n\nconst originalPush = VueRouter.prototype.push;\nVueRouter.prototype.push = function push(location) {\n  return originalPush.call(this, location).catch((err) => err);\n};\n\nconst router = new VueRouter({\n  routes,\n});\n\nexport default router;\n"
  },
  {
    "path": "src/store/getters.js",
    "content": "const getters = {\n  token: state => state.user.token,   // token\n  user: state => state.user.user,     // 用户对象\n}\nexport default getters"
  },
  {
    "path": "src/store/index.js",
    "content": "import Vue from 'vue'\nimport Vuex from 'vuex'\nimport getters from './getters'\nimport user from './modules/user'\n\nVue.use(Vuex)\n\nconst store = new Vuex.Store({\n    modules: {\n        user\n    },\n    getters\n})\n\nexport default store"
  },
  {
    "path": "src/store/modules/user.js",
    "content": "import { getUserInfo, login, logout } from \"@/api/auth/auth\";\nimport { getToken, setToken, removeToken } from \"@/utils/auth\";\n\nconst state = {\n  token: getToken(), // token\n  user: \"\", // 用户对象\n};\n\nconst mutations = {\n  SET_TOKEN_STATE: (state, token) => {\n    state.token = token;\n  },\n  SET_USER_STATE: (state, user) => {\n    state.user = user;\n  },\n};\n\nconst actions = {\n  // 用户登录\n  login({ commit }, userInfo) {\n    console.log(userInfo);\n    const { name, pass, rememberMe } = userInfo;\n    return new Promise((resolve, reject) => {\n      login({ username: name.trim(), password: pass, rememberMe: rememberMe })\n        .then((response) => {\n          const { data } = response;\n          commit(\"SET_TOKEN_STATE\", data.token);\n          setToken(data.token);\n          resolve();\n        })\n        .catch((error) => {\n          reject(error);\n        });\n    });\n  },\n  // 获取用户信息\n  getInfo({ commit, state }) {\n    return new Promise((resolve, reject) => {\n      getUserInfo()\n        .then((response) => {\n          const { data } = response;\n          if (!data) {\n            commit(\"SET_TOKEN_STATE\", \"\");\n            commit(\"SET_USER_STATE\", \"\");\n            removeToken();\n            resolve();\n            reject(\"Verification failed, please Login again.\");\n          }\n          commit(\"SET_USER_STATE\", data);\n          resolve(data);\n        })\n        .catch((error) => {\n          reject(error);\n        });\n    });\n  },\n  // 注销\n  logout({ commit, state }) {\n    return new Promise((resolve, reject) => {\n      logout(state.token)\n        .then((response) => {\n          console.log(response);\n          commit(\"SET_TOKEN_STATE\", \"\");\n          commit(\"SET_USER_STATE\", \"\");\n          removeToken();\n          resolve();\n        })\n        .catch((error) => {\n          reject(error);\n        });\n    });\n  },\n};\n\nexport default {\n  namespaced: true,\n  state,\n  mutations,\n  actions,\n};\n"
  },
  {
    "path": "src/user.js",
    "content": "import request from '@/utils/request'\n\n// 用户主页\nexport function getInfoByName(username, page, size) {\n  return request({\n    url: '/ums/user/' + username,\n    method: 'get',\n    params: {\n      pageNo: page,\n      size: size\n    }\n  })\n}\n\n// 用户主页\nexport function getInfo() {\n  return request({\n    url: '/ums/user/info',\n    method: 'get'\n  })\n}\n\n// 更新\nexport function update(user) {\n  return request({\n    url: '/ums/user/update',\n    method: 'post',\n    data: user\n  })\n}\n"
  },
  {
    "path": "src/utils/auth.js",
    "content": "import Cookies from 'js-cookie'\n\nconst uToken = 'u_token'\nconst darkMode = 'dark_mode';\n\n// 获取Token\nexport function getToken() {\n    return Cookies.get(uToken);\n}\n\n// 设置Token，1天,与后端同步\nexport function setToken(token) {\n    return Cookies.set(uToken, token, {expires: 1})\n}\n\n// 删除Token\nexport function removeToken() {\n    return Cookies.remove(uToken)\n}\n\nexport function removeAll() {\n    return Cookies.Cookies.removeAll()\n}\n\nexport function setDarkMode(mode) {\n    return Cookies.set(darkMode, mode, {expires: 365})\n}\n\nexport function getDarkMode() {\n    return !(undefined === Cookies.get(darkMode) || 'false' === Cookies.get(darkMode));\n}\n"
  },
  {
    "path": "src/utils/get-page-title.js",
    "content": "const title = '小而美的智慧社区系统'\n\nexport default function getPageTitle(pageTitle) {\n    if (pageTitle) {\n        return `${pageTitle} - ${title}`\n    }\n    return `${title}`\n}\n"
  },
  {
    "path": "src/utils/request.js",
    "content": "import axios from 'axios'\nimport { Message, MessageBox } from 'element-ui'\nimport store from '@/store'\nimport { getToken } from '@/utils/auth'\n\n// 1.创建axios实例\nconst service = axios.create({\n  // 公共接口--这里注意后面会讲,url = base url + request url\n  baseURL: process.env.VUE_APP_SERVER_URL,\n\n  // baseURL: 'https://api.example.com',\n  // 超时时间 单位是ms，这里设置了5s的超时时间\n  timeout: 5 * 1000\n})\n\n// 2.请求拦截器request interceptor\nservice.interceptors.request.use(\n  config => {\n    // 发请求前做的一些处理，数据转化，配置请求头，设置token,设置loading等，根据需求去添加\n    // 注意使用token的时候需要引入cookie方法或者用本地localStorage等方法，推荐js-cookie\n    if (store.getters.token) {\n      // config.params = {'token': token}    // 如果要求携带在参数中\n      // config.headers.token = token;       // 如果要求携带在请求头中\n      // bearer：w3c规范\n      config.headers['Authorization'] = 'Bearer ' + getToken()\n    }\n    return config\n  },\n  error => {\n    // do something with request error\n    // console.log(error) // for debug\n    return Promise.reject(error)\n  }\n)\n\n// 设置cross跨域 并设置访问权限 允许跨域携带cookie信息,使用JWT可关闭\nservice.defaults.withCredentials = false\n\nservice.interceptors.response.use(\n  // 接收到响应数据并成功后的一些共有的处理，关闭loading等\n  response => {\n    const res = response.data\n    // 如果自定义代码不是200，则将其判断为错误。\n    if (res.code !== 200) {\n      // 50008: 非法Token; 50012: 异地登录; 50014: Token失效;\n      if (res.code === 401 || res.code === 50012 || res.code === 50014) {\n        // 重新登录\n        MessageBox.confirm('会话失效，您可以留在当前页面，或重新登录', '权限不足', {\n          confirmButtonText: '确定',\n          cancelButtonText: '取消',\n          type: 'warning',\n          center: true\n        }).then(() => {\n          window.location.href = '#/login'\n        })\n      } else { // 其他异常直接提示\n        Message({\n          showClose: true,\n          message: '⚠' + res.message || 'Error',\n          type: 'error',\n          duration: 3 * 1000\n        })\n      }\n      return Promise.reject(new Error(res.message || 'Error'))\n    } else {\n      return res\n    }\n  },\n  error => {\n    /** *** 接收到异常响应的处理开始 *****/\n    // console.log('err' + error) // for debug\n    Message({\n      showClose: true,\n      message: error.message,\n      type: 'error',\n      duration: 5 * 1000\n    })\n    return Promise.reject(error)\n  }\n)\nexport default service"
  },
  {
    "path": "src/utils/scroll-to.js",
    "content": "Math.easeInOutQuad = function(t, b, c, d) {\n  t /= d / 2\n  if (t < 1) {\n    return c / 2 * t * t + b\n  }\n  t--\n  return -c / 2 * (t * (t - 2) - 1) + b\n}\n\n// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts\nvar requestAnimFrame = (function() {\n  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }\n})()\n\n/**\n * Because it's so fucking difficult to detect the scrolling element, just move them all\n * @param {number} amount\n */\nfunction move(amount) {\n  document.documentElement.scrollTop = amount\n  document.body.parentNode.scrollTop = amount\n  document.body.scrollTop = amount\n}\n\nfunction position() {\n  return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop\n}\n\n/**\n * @param {number} to\n * @param {number} duration\n * @param {Function} callback\n */\nexport function scrollTo(to, duration, callback) {\n  const start = position()\n  const change = to - start\n  const increment = 20\n  let currentTime = 0\n  duration = (typeof (duration) === 'undefined') ? 500 : duration\n  var animateScroll = function() {\n    // increment the time\n    currentTime += increment\n    // find the value with the quadratic in-out easing function\n    var val = Math.easeInOutQuad(currentTime, start, change, duration)\n    // move the document.body\n    move(val)\n    // do the animation unless its over\n    if (currentTime < duration) {\n      requestAnimFrame(animateScroll)\n    } else {\n      if (callback && typeof (callback) === 'function') {\n        // the animation is done so lets callback\n        callback()\n      }\n    }\n  }\n  animateScroll()\n}\n"
  },
  {
    "path": "src/views/Home.vue",
    "content": "<template>\n  <div>\n    <div class=\"box\">🔔 {{ billboard.content }}</div>\n    <div class=\"columns\">\n      <div class=\"column is-three-quarters\">\n        <TopicList></TopicList>\n      </div>\n      <div class=\"column\">\n        <CardBar></CardBar>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { getBillboard } from \"@/api/billboard\";\nimport CardBar from \"@/views/card/CardBar\"\nimport PostList from '@/views/post/Index'\n\nexport default {\n  name: \"Home\",\n  components: {CardBar, TopicList: PostList},\n  data() {\n    return {\n      billboard: {\n        content: \"\",\n      },\n    };\n  },\n  created() {\n    this.fetchBillboard();\n  },\n  methods: {\n    async fetchBillboard() {\n      getBillboard().then((value) => {\n        const { data } = value;\n        this.billboard = data;\n      });\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "src/views/Search.vue",
    "content": "<template>\n  <div>\n    <el-card shadow=\"never\">\n      <div slot=\"header\" class=\"clearfix\">\n        检索到 <code>{{ list.length }}</code>\n        条关于 <code class=\"has-text-info\">{{ query.keyword }}</code> 的记录\n      </div>\n      <div>\n        <article v-for=\"(item, index) in list\" :key=\"index\" class=\"media\">\n          <div class=\"media-left\">\n            <figure class=\"image is-48x48\">\n              <img :src=\"`https://cn.gravatar.com/avatar/${item.userId}?s=164&d=monsterid`\"> \n            </figure>\n          </div>\n          <div class=\"media-content\">\n            <div class=\"\">\n              <p class=\"ellipsis is-ellipsis-1\">\n                <el-tooltip class=\"item\" effect=\"dark\" :content=\"item.title\" placement=\"top\">\n                  <router-link :to=\"{name:'post-detail',params:{id:item.id}}\">\n                    <span class=\"is-size-6\">{{ item.title }}</span>\n                  </router-link>\n                </el-tooltip>\n              </p>\n            </div>\n            <nav class=\"level has-text-grey is-mobile  is-size-7 mt-2\">\n              <div class=\"level-left\">\n                <div class=\"level-left\">\n                  <router-link class=\"level-item\" :to=\"{ path: `/member/${item.username}/home` }\">\n                    {{ item.alias }}\n                  </router-link>\n\n                  <span class=\"mr-1\">\n                    发布于:{{ dayjs(item.createTime).format(\"YYYY/MM/DD\") }}\n                  </span>\n\n                  <span\n                    v-for=\"(tag, index) in item.tags\"\n                    :key=\"index\"\n                    class=\"tag is-hidden-mobile is-success is-light mr-1\"\n                  >\n                    <router-link :to=\"{ name: 'tag', params: { name: tag.name } }\">\n                      {{ \"#\" + tag.name }}\n                    </router-link>\n                  </span>\n\n                  <span class=\"is-hidden-mobile\">浏览:{{ item.view }}</span>\n                </div>\n              </div>\n            </nav>\n          </div>\n          <div class=\"media-right\" />\n        </article>\n      </div>\n\n      <!--分页-->\n      <pagination\n        v-show=\"query.total > 0\"\n        :total=\"query.total\"\n        :page.sync=\"query.pageNum\"\n        :limit.sync=\"query.pageSize\"\n        @pagination=\"fetchList\"\n      />\n    </el-card>\n  </div>\n</template>\n\n<script>\nimport { searchByKeyword } from '@/api/search'\nimport Pagination from '@/components/Pagination'\n\nexport default {\n  name: 'Search',\n  components: { Pagination },\n  data() {\n    return {\n      list: [],\n      query: {\n        keyword: this.$route.query.key,\n        pageNum: 1,\n        pageSize: 10,\n        total: 0\n      }\n    }\n  },\n  created() {\n    this.fetchList()\n  },\n  methods: {\n    fetchList() {\n      searchByKeyword(this.query).then(value => {\n        const { data } = value\n        this.list = data.records\n        this.query.total = data.total\n        this.query.pageSize = data.size\n        this.query.pageNum = data.current\n      })\n    }\n  }\n}\n</script>\n\n<style scoped>\n\n</style>\n"
  },
  {
    "path": "src/views/auth/Login.vue",
    "content": "<template>\n  <div class=\"columns py-6\">\n    <div class=\"column is-half is-offset-one-quarter\">\n      <el-card shadow=\"never\">\n        <div slot=\"header\" class=\"has-text-centered has-text-weight-bold\">\n          用户登录\n        </div>\n        <div>\n          <el-form\n            v-loading=\"loading\"\n            :model=\"ruleForm\"\n            status-icon\n            :rules=\"rules\"\n            ref=\"ruleForm\"\n            label-width=\"100px\"\n            class=\"demo-ruleForm\"\n          >\n            <el-form-item label=\"账号\" prop=\"name\">\n              <el-input v-model=\"ruleForm.name\"></el-input>\n            </el-form-item>\n\n            <el-form-item label=\"密码\" prop=\"pass\">\n              <el-input\n                type=\"password\"\n                v-model=\"ruleForm.pass\"\n                autocomplete=\"off\"\n              ></el-input>\n            </el-form-item>\n\n            <el-form-item label=\"记住\" prop=\"delivery\">\n              <el-switch v-model=\"ruleForm.rememberMe\"></el-switch>\n            </el-form-item>\n\n            <el-form-item>\n              <el-button type=\"primary\" @click=\"submitForm('ruleForm')\"\n                >提交</el-button\n              >\n              <el-button @click=\"resetForm('ruleForm')\">重置</el-button>\n            </el-form-item>\n          </el-form>\n        </div>\n      </el-card>\n    </div>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: \"Login\",\n  data() {\n    return {\n      redirect: undefined,\n      loading: false,\n      ruleForm: {\n        name: \"\",\n        pass: \"\",\n        rememberMe: true,\n      },\n      rules: {\n        name: [\n          { required: true, message: \"请输入账号\", trigger: \"blur\" },\n          {\n            min: 2,\n            max: 15,\n            message: \"长度在 2 到 15 个字符\",\n            trigger: \"blur\",\n          },\n        ],\n        pass: [\n          { required: true, message: \"请输入密码\", trigger: \"blur\" },\n          {\n            min: 6,\n            max: 20,\n            message: \"长度在 6 到 20 个字符\",\n            trigger: \"blur\",\n          },\n        ],\n      },\n    };\n  },\n  methods: {\n    submitForm(formName) {\n      this.$refs[formName].validate((valid) => {\n        if (valid) {\n          this.loading = true;\n          this.$store\n            .dispatch(\"user/login\", this.ruleForm)\n            .then(() => {\n              this.$message({\n                message: \"恭喜你，登录成功\",\n                type: \"success\",\n                duration: 2000,\n              });\n\n              setTimeout(() => {\n                this.loading = false;\n                this.$router.push({ path: this.redirect || \"/\" });\n              }, 0.1 * 1000);\n            })\n            .catch(() => {\n              this.loading = false;\n            });\n        } else {\n          return false;\n        }\n      });\n    },\n    resetForm(formName) {\n      this.$refs[formName].resetFields();\n    },\n  },\n};\n</script>\n\n<style scoped>\n</style>"
  },
  {
    "path": "src/views/auth/Register.vue",
    "content": "<template>\n  <div class=\"columns py-6\">\n    <div class=\"column is-half is-offset-one-quarter\">\n      <el-card shadow=\"never\">\n        <div slot=\"header\" class=\"has-text-centered has-text-weight-bold\">\n          新用户入驻\n        </div>\n        <div>\n          <el-form\n            ref=\"ruleForm\"\n            v-loading=\"loading\"\n            :model=\"ruleForm\"\n            status-icon\n            :rules=\"rules\"\n            label-width=\"100px\"\n            class=\"demo-ruleForm\"\n          >\n            <el-form-item label=\"账号\" prop=\"name\">\n              <el-input v-model=\"ruleForm.name\" />\n            </el-form-item>\n\n            <el-form-item label=\"密码\" prop=\"pass\">\n              <el-input\n                v-model=\"ruleForm.pass\"\n                type=\"password\"\n                autocomplete=\"off\"\n              />\n            </el-form-item>\n\n            <el-form-item label=\"确认密码\" prop=\"checkPass\">\n              <el-input\n                v-model=\"ruleForm.checkPass\"\n                type=\"password\"\n                autocomplete=\"off\"\n              />\n            </el-form-item>\n\n            <el-form-item label=\"邮箱\" prop=\"email\">\n              <el-input v-model=\"ruleForm.email\" autocomplete=\"off\" />\n            </el-form-item>\n\n            <el-form-item>\n              <el-button\n                type=\"primary\"\n                @click=\"submitForm('ruleForm')\"\n              >立即注册</el-button>\n              <el-button @click=\"resetForm('ruleForm')\">重置</el-button>\n            </el-form-item>\n          </el-form>\n        </div>\n      </el-card>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { userRegister } from '@/api/auth/auth'\n\nexport default {\n  name: 'Register',\n  data() {\n    const validatePass = (rule, value, callback) => {\n      if (value === '') {\n        callback(new Error('请再次输入密码'))\n      } else if (value !== this.ruleForm.pass) {\n        callback(new Error('两次输入密码不一致!'))\n      } else {\n        callback()\n      }\n    }\n    return {\n      loading: false,\n      ruleForm: {\n        name: '',\n        pass: '',\n        checkPass: '',\n        email: ''\n      },\n      rules: {\n        name: [\n          { required: true, message: '请输入账号', trigger: 'blur' },\n          {\n            min: 2,\n            max: 10,\n            message: '长度在 2 到 10 个字符',\n            trigger: 'blur'\n          }\n        ],\n        pass: [\n          { required: true, message: '请输入密码', trigger: 'blur' },\n          {\n            min: 6,\n            max: 20,\n            message: '长度在 6 到 20 个字符',\n            trigger: 'blur'\n          }\n        ],\n        checkPass: [\n          { required: true, message: '请再次输入密码', trigger: 'blur' },\n          { validator: validatePass, trigger: 'blur' }\n        ],\n        email: [\n          { required: true, message: '请输入邮箱地址', trigger: 'blur' },\n          {\n            type: 'email',\n            message: '请输入正确的邮箱地址',\n            trigger: ['blur', 'change']\n          }\n        ]\n      }\n    }\n  },\n  methods: {\n    submitForm(formName) {\n      this.$refs[formName].validate((valid) => {\n        if (valid) {\n          this.loading = true\n          userRegister(this.ruleForm)\n            .then((value) => {\n              const { code, message } = value\n              if (code === 200) {\n                this.$message({\n                  message: '账号注册成功',\n                  type: 'success'\n                })\n                setTimeout(() => {\n                  this.loading = false\n                  this.$router.push({ path: this.redirect || '/login' })\n                }, 0.1 * 1000)\n              } else {\n                this.$message.error('注册失败，' + message)\n              }\n            })\n            .catch(() => {\n              this.loading = false\n            })\n        } else {\n          return false\n        }\n      })\n    },\n    resetForm(formName) {\n      this.$refs[formName].resetFields()\n    }\n  }\n}\n</script>\n\n<style scoped>\n</style>"
  },
  {
    "path": "src/views/card/CardBar.vue",
    "content": "<template>\n  <section>\n    <!--是否登录-->\n    <login-welcome />\n\n    <!--今日赠言-->\n    <tip-card />\n\n    <!--资源推介-->\n    <PromotionCard />\n  </section>\n</template>\n\n<script>\nimport TipCard from '@/views/card/Tip'\nimport PromotionCard from '@/views/card/Promotion'\nimport LoginWelcome from '@/views/card/LoginWelcome'\n\nexport default {\n  name: 'CardBar',\n  components: { LoginWelcome, PromotionCard, TipCard }\n}\n</script>\n\n<style scoped>\n</style>\n"
  },
  {
    "path": "src/views/card/LoginWelcome.vue",
    "content": "<template>\n  <el-card class=\"box-card\" shadow=\"never\">\n    <div slot=\"header\">\n      <span>💐 发帖</span>\n    </div>\n    <div v-if=\"token != null && token !== ''\" class=\"has-text-centered\">\n      <b-button type=\"is-danger\" tag=\"router-link\" :to=\"{path:'/post/create'}\" outlined>✍ 发表想法</b-button>\n    </div>\n\n    <div v-else class=\"has-text-centered\">\n      <b-button type=\"is-primary\" tag=\"router-link\" :to=\"{path:'/register'}\" outlined>马上入驻</b-button>\n      <b-button type=\"is-danger\" tag=\"router-link\" :to=\"{path:'/login'}\" outlined class=\"ml-2\"> 社区登入</b-button>\n    </div>\n  </el-card>\n</template>\n\n<script>\nimport { mapGetters } from 'vuex'\n\nexport default {\n  name: 'LoginWelcome',\n  computed: {\n    ...mapGetters([\n      'token'\n    ])\n  }\n}\n</script>\n\n<style scoped>\n\n</style>"
  },
  {
    "path": "src/views/card/Promotion.vue",
    "content": "<template>\n  <el-card class=\"box-card\" shadow=\"never\">\n    <div slot=\"header\">\n      <span>🥂 推广</span>\n    </div>\n    <div>\n      <p v-for=\"(item, index) in list\" :key=\"index\" class=\"block\">\n        <a :href=\"item.link\" target=\"_blank\">{{ item.title }}</a>\n      </p>\n    </div>\n  </el-card>\n</template>\n\n<script>\nimport { getList } from '@/api/promote'\n\nexport default {\n  name: 'Promotion',\n  data() {\n    return {\n      list: []\n    }\n  },\n  created() {\n    this.fetchList()\n  },\n  methods: {\n    fetchList() {\n      getList().then((response) => {\n        const { data } = response\n        this.list = data\n      })\n    }\n  }\n}\n</script>\n\n<style scoped>\n</style>\n"
  },
  {
    "path": "src/views/card/Tip.vue",
    "content": "<template>\n  <el-card class=\"box-card\" shadow=\"never\">\n    <div slot=\"header\">\n      <span>🥳 每日一句</span>\n    </div>\n    <div>\n      <div class=\"has-text-left block\">\n        {{ tip.content }}\n      </div>\n      <div class=\"has-text-right mt-5 block\">\n        ——{{ tip.author }}\n      </div>\n    </div>\n  </el-card>\n</template>\n\n<script>\nimport {getTodayTip} from '@/api/tip'\n\nexport default {\n  name: 'Tip',\n  data() {\n    return {\n      tip: {}\n    }\n  },\n  created() {\n    this.fetchTodayTip()\n  },\n  methods: {\n    fetchTodayTip() {\n      getTodayTip().then(response => {\n        const { data } = response\n        this.tip = data\n      })\n    }\n  }\n}\n</script>\n\n<style scoped>\n\n</style>\n"
  },
  {
    "path": "src/views/error/404.vue",
    "content": "<template>\n  <div class=\"columns mt-6\">\n    <div class=\"column mt-6\">\n      <div class=\"mt-6\">\n        <p class=\"content\">UH OH! 页面丢失</p>\n        <p class=\"content subtitle mt-6\">\n          您所寻找的页面不存在， {{ times }} 秒后，将返回首页!\n        </p>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: \"404\",\n  data() {\n    return {\n      times: 10\n    }\n  },\n  created() {\n    this.goHome();\n  },\n  methods: {\n    goHome: function () {\n      this.timer = setInterval(() => {\n        this.times--\n        if (this.times === 0) {\n          clearInterval(this.timer)\n          this.$router.push({path: '/'});\n        }\n      }, 1000)\n    }\n  }\n}\n</script>\n\n<style scoped>\n</style>"
  },
  {
    "path": "src/views/post/Author.vue",
    "content": "<template>\n  <section id=\"author\">\n    <el-card class=\"\" shadow=\"never\">\n      <div slot=\"header\">\n        <span class=\"has-text-weight-bold\">👨‍💻 关于作者</span>\n      </div>\n      <div class=\"has-text-centered\">\n        <p class=\"is-size-5 mb-5\">\n          <router-link :to=\"{ path: `/member/${user.username}/home` }\">\n            {{ user.alias }} <span class=\"is-size-7 has-text-grey\">{{ '@' + user.username }}</span>\n          </router-link>\n        </p>\n        <div class=\"columns is-mobile\">\n          <div class=\"column is-half\">\n            <code>{{ user.topicCount }}</code>\n            <p>文章</p>\n          </div>\n          <div class=\"column is-half\">\n            <code>{{ user.followerCount }}</code>\n            <p>粉丝</p>\n          </div>\n        </div>\n        <div>\n          <button\n            v-if=\"hasFollow\"\n            class=\"button is-success button-center is-fullwidth\"\n            @click=\"handleUnFollow(user.id)\"\n          >\n            已关注\n          </button>\n\n          <button v-else class=\"button is-link button-center is-fullwidth\" @click=\"handleFollow(user.id)\">\n            关注\n          </button>\n        </div>\n      </div>\n    </el-card>\n  </section>\n</template>\n\n<script>\nimport { follow, hasFollow, unFollow } from '@/api/follow'\nimport { mapGetters } from 'vuex'\nexport default {\n  name: 'Author',\n  props: {\n    user: {\n      type: Object,\n      default: null\n    }\n  },\n  data() {\n    return {\n      hasFollow: false\n    }\n  },\n  mounted() {\n    this.fetchInfo()\n  },\n  computed: {\n    ...mapGetters([\n      'token'\n    ])\n  },\n  methods: {\n    fetchInfo() {\n      if(this.token != null && this.token !== '')\n      {\n        hasFollow(this.user.id).then(value => {\n          const { data } = value\n          this.hasFollow = data.hasFollow\n        })\n      }\n    },\n    handleFollow: function(id) {\n      if(this.token != null && this.token !== '')\n      {\n        follow(id).then(response => {\n          const { message } = response\n          this.$message.success(message)\n          this.hasFollow = !this.hasFollow\n          this.user.followerCount = parseInt(this.user.followerCount) + 1\n        })\n      }\n      else{\n        this.$message.success('请先登录')\n      }\n    },\n    handleUnFollow: function(id) {\n      unFollow(id).then(response => {\n        const { message } = response\n        this.$message.success(message)\n        this.hasFollow = !this.hasFollow\n        this.user.followerCount = parseInt(this.user.followerCount) - 1\n      })\n    }\n  }\n}\n</script>\n\n<style scoped>\n\n</style>\n"
  },
  {
    "path": "src/views/post/Create.vue",
    "content": "<template>\n  <div class=\"columns\">\n    <div class=\"column is-full\">\n      <el-card\n        class=\"box-card\"\n        shadow=\"never\"\n      >\n        <div\n          slot=\"header\"\n          class=\"clearfix\"\n        >\n          <span><i class=\"fa fa fa-book\"> 主题 / 发布主题</i></span>\n        </div>\n        <div>\n          <el-form\n            ref=\"ruleForm\"\n            :model=\"ruleForm\"\n            :rules=\"rules\"\n            class=\"demo-ruleForm\"\n          >\n            <el-form-item prop=\"title\">\n              <el-input\n                v-model=\"ruleForm.title\"\n                placeholder=\"输入主题名称\"\n              />\n            </el-form-item>\n\n            <!--Markdown-->\n            <div id=\"vditor\" />\n\n            <b-taginput\n              v-model=\"ruleForm.tags\"\n              class=\"my-3\"\n              maxlength=\"15\"\n              maxtags=\"3\"\n              ellipsis\n              placeholder=\"请输入主题标签，限制为 15 个字符和 3 个标签\"\n            />\n\n            <el-form-item>\n              <el-button\n                type=\"primary\"\n                @click=\"submitForm('ruleForm')\"\n              >立即创建\n              </el-button>\n              <el-button @click=\"resetForm('ruleForm')\">重置</el-button>\n            </el-form-item>\n          </el-form>\n        </div>\n      </el-card>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { post } from '@/api/post'\nimport Vditor from 'vditor'\nimport 'vditor/dist/index.css'\n\nexport default {\n  name: 'TopicPost',\n\n  data() {\n    return {\n      contentEditor: {},\n      ruleForm: {\n        title: '', // 标题\n        tags: [], // 标签\n        content: '' // 内容\n      },\n      rules: {\n        title: [\n          { required: true, message: '请输入话题名称', trigger: 'blur' },\n          {\n            min: 1,\n            max: 25,\n            message: '长度在 1 到 25 个字符',\n            trigger: 'blur'\n          }\n        ]\n      }\n    }\n  },\n  mounted() {\n    this.contentEditor = new Vditor('vditor', {\n      height: 500,\n      placeholder: '此处为话题内容……',\n      theme: 'classic',\n      counter: {\n        enable: true,\n        type: 'markdown'\n      },\n      preview: {\n        delay: 0,\n        hljs: {\n          style: 'monokai',\n          lineNumber: true\n        }\n      },\n      tab: '\\t',\n      typewriterMode: true,\n      toolbarConfig: {\n        pin: true\n      },\n      cache: {\n        enable: false\n      },\n      mode: 'sv'\n    })\n  },\n  methods: {\n    submitForm(formName) {\n      this.$refs[formName].validate((valid) => {\n        if (valid) {\n          if (\n            this.contentEditor.getValue().length === 1 ||\n            this.contentEditor.getValue() == null ||\n            this.contentEditor.getValue() === ''\n          ) {\n            alert('话题内容不可为空')\n            return false\n          }\n          if (this.ruleForm.tags == null || this.ruleForm.tags.length === 0) {\n            alert('标签不可以为空')\n            return false\n          }\n          this.ruleForm.content = this.contentEditor.getValue()\n          post(this.ruleForm).then((response) => {\n            const { data } = response\n            setTimeout(() => {\n              this.$router.push({\n                name: 'post-detail',\n                params: { id: data.id }\n              })\n            }, 800)\n          })\n        } else {\n          console.log('error submit!!')\n          return false\n        }\n      })\n    },\n    resetForm(formName) {\n      this.$refs[formName].resetFields()\n      this.contentEditor.setValue('')\n      this.ruleForm.tags = ''\n    }\n  }\n}\n</script>\n\n<style>\n</style>\n"
  },
  {
    "path": "src/views/post/Detail.vue",
    "content": "<template>\n  <div class=\"columns\">\n    <!--文章详情-->\n    <div class=\"column is-three-quarters\">\n      <!--主题-->\n      <el-card\n        class=\"box-card\"\n        shadow=\"never\"\n      >\n        <div\n          slot=\"header\"\n          class=\"has-text-centered\"\n        >\n          <p class=\"is-size-5 has-text-weight-bold\">{{ topic.title }}</p>\n          <div class=\"has-text-grey is-size-7 mt-3\">\n            <span>{{ dayjs(topic.createTime).format('YYYY/MM/DD HH:mm:ss') }}</span>\n            <el-divider direction=\"vertical\" />\n            <span>发布者：{{ topicUser.alias }}</span>\n            <el-divider direction=\"vertical\" />\n            <span>查看：{{ topic.view }}</span>\n          </div>\n        </div>\n\n        <!--Markdown-->\n        <div id=\"preview\" />\n\n        <!--标签-->\n        <nav class=\"level has-text-grey is-size-7 mt-6\">\n          <div class=\"level-left\">\n            <p class=\"level-item\">\n              <b-taglist>\n                <router-link\n                  v-for=\"(tag, index) in tags\"\n                  :key=\"index\"\n                  :to=\"{ name: 'tag', params: { name: tag.name } }\"\n                >\n                  <b-tag type=\"is-info is-light mr-1\">\n                    {{ \"#\" + tag.name }}\n                  </b-tag>\n                </router-link>\n              </b-taglist>\n            </p>\n          </div>\n          <div\n            v-if=\"token && user.id === topicUser.id\"\n            class=\"level-right\"\n          >\n            <router-link\n              class=\"level-item\"\n              :to=\"{name:'topic-edit',params: {id:topic.id}}\"\n            >\n              <span class=\"tag\">编辑</span>\n            </router-link>\n            <a class=\"level-item\">\n              <span\n                class=\"tag\"\n                @click=\"handleDelete(topic.id)\"\n              >删除</span>\n            </a>\n          </div>\n        </nav>\n      </el-card>\n\n      <lv-comments :slug=\"topic.id\" /> \n    </div>\n\n    <div class=\"column\">\n      <!--作者-->\n      <Author\n        v-if=\"flag\"\n        :user=\"topicUser\"\n      />\n      <!--推荐-->\n      <recommend\n        v-if=\"flag\"\n        :topic-id=\"topic.id\"\n      />\n    </div>\n  </div>\n</template>\n\n<script>\nimport { deleteTopic, getTopic } from '@/api/post'\nimport { mapGetters } from 'vuex'\nimport Author from '@/views/post/Author'\nimport Recommend from '@/views/post/Recommend'\nimport LvComments from '@/components/Comment/Comments'\nimport Vditor from 'vditor'\nimport 'vditor/dist/index.css'\n\nexport default {\n  name: 'TopicDetail',\n  components: { Author, Recommend, LvComments },\n  computed: {\n    ...mapGetters([\n      'token','user'\n    ])\n  },\n  data() {\n    return {\n      flag: false,\n      topic: {\n        content: '',\n        id: this.$route.params.id\n      },\n      tags: [],\n      topicUser: {}\n    }\n  },\n  mounted() {\n    this.fetchTopic()\n  },\n  methods: {\n    renderMarkdown(md) {\n      Vditor.preview(document.getElementById('preview'), md, {\n        hljs: { style: 'github' }\n      })\n    },\n    // 初始化\n    async fetchTopic() {\n      getTopic(this.$route.params.id).then(response => {\n        const { data } = response\n        document.title = data.topic.title\n\n        this.topic = data.topic\n        this.tags = data.tags\n        this.topicUser = data.user\n        // this.comments = data.comments\n        this.renderMarkdown(this.topic.content)\n        this.flag = true\n      })\n    },\n    handleDelete(id) {\n      deleteTopic(id).then(value => {\n        const { code, message } = value\n        alert(message)\n\n        if (code === 200) {\n          setTimeout(() => {\n            this.$router.push({ path: '/' })\n          }, 500)\n        }\n      })\n    }\n  }\n}\n</script>\n\n<style>\n#preview {\n  min-height: 300px;\n}\n</style>\n"
  },
  {
    "path": "src/views/post/Edit.vue",
    "content": "<template>\n  <section>\n    <div class=\"columns\">\n      <div class=\"column is-full\">\n        <el-card class=\"box-card\" shadow=\"never\">\n          <div slot=\"header\" class=\"clearfix\">\n            <span><i class=\"fa fa fa-book\"> 主题 / 更新主题</i></span>\n          </div>\n          <div>\n            <el-form :model=\"topic\" ref=\"topic\" class=\"demo-topic\">\n              <el-form-item prop=\"title\">\n                <el-input\n                  v-model=\"topic.title\"\n                  placeholder=\"输入新的主题名称\"\n                ></el-input>\n              </el-form-item>\n\n              <!--Markdown-->\n              <div id=\"vditor\"></div>\n\n              <b-taginput\n                v-model=\"tags\"\n                class=\"my-3\"\n                maxlength=\"15\"\n                maxtags=\"3\"\n                ellipsis\n                placeholder=\"请输入主题标签，限制为 15 个字符和 3 个标签\"\n              />\n              <el-form-item class=\"mt-3\">\n                <el-button type=\"primary\" @click=\"handleUpdate()\"\n                  >更新\n                </el-button>\n                <el-button @click=\"resetForm('topic')\">重置</el-button>\n              </el-form-item>\n            </el-form>\n          </div>\n        </el-card>\n      </div>\n    </div>\n  </section>\n</template>\n\n<script>\nimport { getTopic, update } from \"@/api/post\";\nimport Vditor from \"vditor\";\nimport \"vditor/dist/index.css\";\n\nexport default {\n  name: \"TopicEdit\",\n  components: {},\n  data() {\n    return {\n      topic: {},\n      tags: [],\n    };\n  },\n  created() {\n    this.fetchTopic();\n  },\n  methods: {\n    renderMarkdown(md) {\n      this.contentEditor = new Vditor(\"vditor\", {\n        height: 460,\n        placeholder: \"输入要更新的内容\",\n        preview: {\n          hljs: { style: \"monokai\" },\n        },\n        mode: \"sv\",\n        after: () => {\n          this.contentEditor.setValue(md);\n        },\n      });\n    },\n    fetchTopic() {\n      getTopic(this.$route.params.id).then((value) => {\n        this.topic = value.data.topic;\n        this.tags = value.data.tags.map(tag => tag.name);\n        this.renderMarkdown(this.topic.content);\n      });\n    },\n    handleUpdate: function () {\n      this.topic.content = this.contentEditor.getValue();\n      update(this.topic).then((response) => {\n        const { data } = response;\n        console.log(data);\n        setTimeout(() => {\n          this.$router.push({\n            name: \"post-detail\",\n            params: { id: data.id },\n          });\n        }, 800);\n      });\n    },\n\n    resetForm(formName) {\n      this.$refs[formName].resetFields();\n      this.contentEditor.setValue(\"\");\n      this.tags = \"\";\n    },\n  },\n};\n</script>\n\n<style>\n.vditor-reset pre > code {\n  font-size: 100%;\n}\n</style>\n"
  },
  {
    "path": "src/views/post/Index.vue",
    "content": "<template>\n  <div>\n    <el-card shadow=\"never\">\n      <div slot=\"header\" class=\"clearfix\">\n        <el-tabs v-model=\"activeName\" @tab-click=\"handleClick\">\n          <el-tab-pane label=\"最新主题\" name=\"latest\">\n            <article v-for=\"(item, index) in articleList\" :key=\"index\" class=\"media\">\n              <div class=\"media-left\">\n                <figure class=\"image is-48x48\">\n                  <img :src=\"`https://cn.gravatar.com/avatar/${item.userId}?s=164&d=monsterid`\" style=\"border-radius: 5px;\">\n                </figure>\n              </div>\n              <div class=\"media-content\">\n                <div class=\"\">\n                  <p class=\"ellipsis is-ellipsis-1\">\n                    <el-tooltip class=\"item\" effect=\"dark\" :content=\"item.title\" placement=\"top\">\n                      <router-link :to=\"{name:'post-detail',params:{id:item.id}}\">\n                        <span class=\"is-size-6\">{{ item.title }}</span>\n                      </router-link>\n                    </el-tooltip>\n                  </p>\n                </div>\n                <nav class=\"level has-text-grey is-mobile  is-size-7 mt-2\">\n                  <div class=\"level-left\">\n                    <div class=\"level-left\">\n                      <router-link class=\"level-item\" :to=\"{ path: `/member/${item.username}/home` }\">\n                        {{ item.alias }}\n                      </router-link>\n\n                      <span class=\"mr-1\">\n                        发布于:{{ dayjs(item.createTime).format(\"YYYY/MM/DD\") }}\n                      </span>\n\n                      <span\n                        v-for=\"(tag, index) in item.tags\"\n                        :key=\"index\"\n                        class=\"tag is-hidden-mobile is-success is-light mr-1\"\n                      >\n                        <router-link :to=\"{ name: 'tag', params: { name: tag.name } }\">\n                          {{ \"#\" + tag.name }}\n                        </router-link>\n                      </span>\n\n                      <span class=\"is-hidden-mobile\">浏览:{{ item.view }}</span>\n                    </div>\n                  </div>\n                </nav>\n              </div>\n              <div class=\"media-right\" />\n            </article>\n          </el-tab-pane>\n          <el-tab-pane label=\"热门主题\" name=\"hot\">\n            <article v-for=\"(item, index) in articleList\" :key=\"index\" class=\"media\">\n              <div class=\"media-left\">\n                <figure class=\"image is-48x48\">\n                  <img :src=\"`https://cn.gravatar.com/avatar/${item.userId}?s=164&d=monsterid`\" style=\"border-radius: 5px;\">\n                </figure>\n              </div>\n              <div class=\"media-content\">\n                <div class=\"\">\n                  <p class=\"ellipsis is-ellipsis-1\">\n                    <el-tooltip class=\"item\" effect=\"dark\" :content=\"item.title\" placement=\"top\">\n                      <router-link :to=\"{name:'post-detail',params:{id:item.id}}\">\n                        <span class=\"is-size-6\">{{ item.title }}</span>\n                      </router-link>\n                    </el-tooltip>\n                  </p>\n                </div>\n                <nav class=\"level has-text-grey is-mobile  is-size-7 mt-2\">\n                  <div class=\"level-left\">\n                    <div class=\"level-left\">\n                      <router-link class=\"level-item\" :to=\"{ path: `/member/${item.username}/home` }\">\n                        {{ item.alias }}\n                      </router-link>\n\n                      <span class=\"mr-1\">\n                        发布于:{{ dayjs(item.createTime).format(\"YYYY/MM/DD\") }}\n                      </span>\n\n                      <span\n                        v-for=\"(tag, index) in item.tags\"\n                        :key=\"index\"\n                        class=\"tag is-hidden-mobile is-success is-light mr-1\"\n                      >\n                        <router-link :to=\"{ name: 'tag', params: { name: tag.name } }\">\n                          {{ \"#\" + tag.name }}\n                        </router-link>\n                      </span>\n\n                      <span class=\"is-hidden-mobile\">浏览:{{ item.view }}</span>\n                    </div>\n                  </div>\n                </nav>\n              </div>\n              <div class=\"media-right\" />\n            </article>\n          </el-tab-pane>\n        </el-tabs>\n      </div>\n\n      <!--分页-->\n      <pagination\n        v-show=\"page.total > 0\"\n        :total=\"page.total\"\n        :page.sync=\"page.current\"\n        :limit.sync=\"page.size\"\n        @pagination=\"init\"\n      />\n    </el-card>\n  </div>\n</template>\n\n<script>\nimport { getList } from '@/api/post'\nimport Pagination from '@/components/Pagination'\n\nexport default {\n  name: 'TopicList',\n  components: { Pagination },\n  data() {\n    return {\n      activeName: 'latest',\n      articleList: [],\n      page: {\n        current: 1,\n        size: 10,\n        total: 0,\n        tab: 'latest'\n      }\n    }\n  },\n  created() {\n    this.init(this.tab)\n  },\n  methods: {\n    init(tab) {\n      getList(this.page.current, this.page.size, tab).then((response) => {\n        const { data } = response\n        this.page.current = data.current\n        this.page.total = data.total\n        this.page.size = data.size\n        this.articleList = data.records\n      })\n    },\n    handleClick(tab) {\n      this.page.current = 1\n      this.init(tab.name)\n    }\n  }\n}\n</script>\n\n<style scoped>\n\n</style>\n"
  },
  {
    "path": "src/views/post/Recommend.vue",
    "content": "<template>\n  <el-card class=\"\" shadow=\"never\">\n    <div slot=\"header\">\n      <span class=\"has-text-weight-bold\">🧐 随便看看</span>\n    </div>\n    <div>\n      <p v-for=\"(item,index) in recommend\" :key=\"index\" :title=\"item.title\" class=\"block ellipsis is-ellipsis-1\">\n        <router-link :to=\"{name:'post-detail',params: { id: item.id }}\">\n          <span v-if=\"index<9\" class=\"tag\">\n            0{{ parseInt(index) + 1 }}\n          </span>\n          <span v-else class=\"tag\">\n            {{ parseInt(index) + 1 }}\n          </span>\n          {{ item.title }}\n        </router-link>\n      </p>\n    </div>\n  </el-card>\n</template>\n\n<script>\nimport { getRecommendTopics } from '@/api/post'\n\nexport default {\n  name: 'Recommend',\n  props: {\n    topicId: {\n      type: String,\n      default: null\n    }\n  },\n  data() {\n    return {\n      recommend: []\n    }\n  },\n  created() {\n    this.fetchRecommendTopics()\n  },\n  methods: {\n    fetchRecommendTopics() {\n      getRecommendTopics(this.topicId).then(value => {\n        const { data } = value\n        this.recommend = data\n      })\n    }\n  }\n}\n</script>\n\n<style scoped>\n\n</style>\n"
  },
  {
    "path": "src/views/tag/Tag.vue",
    "content": "<template>\n  <div id=\"tag\" class=\"columns\">\n    <div class=\"column is-three-quarters\">\n      <el-card class=\"box-card\" shadow=\"never\">\n        <div slot=\"header\" class=\"\">\n          🔍 检索到 <span class=\"has-text-info\">{{ topics.length }}</span> 篇有关\n          <span class=\"has-text-info\">{{ this.$route.params.name }}</span>\n          的话题\n        </div>\n\n        <div class=\"text item\">\n          <article v-for=\"(item, index) in topics\" :key=\"index\" class=\"media mt-3\">\n            <div class=\"media-content\">\n              <div class=\"content\">\n                <el-tooltip class=\"item\" effect=\"dark\" :content=\"item.title\" placement=\"top\">\n                  <router-link :to=\"{ name: 'post-detail',params:{id: item.id } }\">\n                    {{ item.title }}\n                  </router-link>\n                </el-tooltip>\n              </div>\n\n              <nav class=\"level has-text-grey is-size-7\">\n                <div class=\"level-left\">\n                  <span>发布于:{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>\n\n                  <span class=\"mx-3\">浏览:{{ item.view }}</span>\n\n                  <span>评论:{{ item.comments }}</span>\n                </div>\n              </nav>\n            </div>\n          </article>\n        </div>\n      </el-card>\n    </div>\n\n    <div class=\"column\">\n      <el-card class=\"box-card\" shadow=\"hover\">\n        <div slot=\"header\" class=\"clearfix\">\n          🤙 关于标签\n        </div>\n        <div>\n          <ul>\n            <li class=\"content\">标签由平台用户发布使用</li>\n            <li class=\"content\">系统每周会定时清理无用标签</li>\n          </ul>\n        </div>\n      </el-card>\n      <el-card shadow=\"hover\">\n        <div slot=\"header\">\n          🏷 热门标签\n        </div>\n        <div>\n          <ul>\n            <li v-for=\"(tag,index) in tags\" :key=\"index\" style=\"padding: 6px 0\">\n              <router-link :to=\"{name:'tag',params:{name:tag.name}}\">\n                <span v-if=\"index<9\" class=\"tag\">\n                  0{{ parseInt(index) + 1 }}\n                </span>\n                <span v-else class=\"tag\">\n                  {{ parseInt(index) + 1 }}\n                </span>\n                {{ tag.name }}\n              </router-link>\n            </li>\n          </ul>\n        </div>\n      </el-card>\n    </div>\n  </div>\n\n</template>\n\n<script>\nimport { getTopicsByTag } from '@/api/tag'\n\nexport default {\n  name: 'Tag',\n  data() {\n    return {\n      topics: [],\n      tags: [],\n      paramMap: {\n        name: this.$route.params.name,\n        page: 1,\n        size: 10\n      }\n    }\n  },\n  created() {\n    this.fetchList()\n  },\n  methods: {\n    fetchList: function() {\n      getTopicsByTag(this.paramMap).then(response => {\n        console.log(response)\n        this.topics = response.data.topics.records\n        this.tags = response.data.hotTags.records\n      })\n    }\n  }\n}\n</script>\n\n<style scoped>\n#tag {\n  min-height: 500px;\n}\n</style>\n"
  },
  {
    "path": "src/views/user/Profile.vue",
    "content": "<template>\n  <div class=\"member\">\n    <div class=\"columns\">\n      <div class=\"column is-one-quarter\">\n        <el-card shadow=\"never\">\n          <div slot=\"header\" class=\"has-text-centered\">\n            <el-avatar :size=\"64\" :src=\"`https://cn.gravatar.com/avatar/${topicUser.id}?s=164&d=monsterid`\" />\n            <p class=\"mt-3\">{{ topicUser.alias || topicUser.username }}</p>\n          </div>\n          <div>\n            <p class=\"content\">积分：<code>{{ topicUser.score }}</code></p>\n            <p class=\"content\">入驻：{{ dayjs(topicUser.createTime).format(\"YYYY/MM/DD HH:MM:ss\") }}</p>\n            <p class=\"content\">简介：{{ topicUser.bio }}</p>\n          </div>\n        </el-card>\n      </div>\n\n      <div class=\"column\">\n        <!--用户发布的话题-->\n        <el-card class=\"box-card content\" shadow=\"never\">\n          <div slot=\"header\" class=\"has-text-weight-bold\">\n            <span>话题</span>\n          </div>\n\n          <div v-if=\"topics.length===0\">\n            暂无话题\n          </div>\n\n          <div v-else class=\"topicUser-info\">\n            <article v-for=\"(item, index) in topics\" :key=\"index\" class=\"media\">\n              <div class=\"media-content\">\n                <div class=\"content ellipsis is-ellipsis-1\">\n                  <el-tooltip class=\"item\" effect=\"dark\" :content=\"item.title\" placement=\"top\">\n                    <router-link :to=\"{ name: 'post-detail', params: { id: item.id } }\">\n                      {{ item.title }}\n                    </router-link>\n                  </el-tooltip>\n                </div>\n                <nav class=\"level has-text-grey is-size-7\">\n                  <div class=\"level-left\">\n                    <span class=\"mr-1\">\n                      发布于:{{ dayjs(item.createTime).format(\"YYYY/MM/DD HH:mm:ss\") }}\n                    </span>\n                  </div>\n                </nav>\n              </div>\n              <div v-if=\"token\" class=\"media-right\">\n                <div v-if=\"topicUser.username === user.username\" class=\"level\">\n                  <div class=\"level-item mr-1\">\n                    <router-link :to=\"{name:'topic-edit',params: {id:item.id}}\">\n                      <span class=\"tag is-warning\">编辑</span>\n                    </router-link>\n                  </div>\n                  <div class=\"level-item\">\n                    <a @click=\"handleDelete(item.id)\">\n                      <span class=\"tag is-danger\">删除</span>\n                    </a>\n                  </div>\n                </div>\n              </div>\n            </article>\n          </div>\n\n          <!--分页-->\n          <pagination\n            v-show=\"page.total > 0\"\n            class=\"mt-5\"\n            :total=\"page.total\"\n            :page.sync=\"page.current\"\n            :limit.sync=\"page.size\"\n            @pagination=\"fetchUserById\"\n          />\n        </el-card>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { getInfoByName } from '@/api/user'\nimport pagination from '@/components/Pagination/index'\nimport { mapGetters } from 'vuex'\nimport { deleteTopic } from '@/api/post'\n\nexport default {\n  name: 'Profile',\n  components: { pagination },\n  data() {\n    return {\n      topicUser: {},\n      topics: {},\n      page: {\n        current: 1,\n        size: 5,\n        total: 0\n      }\n    }\n  },\n  computed: {\n    ...mapGetters(['token', 'user'])\n  },\n  created() {\n    this.fetchUserById()\n  },\n  methods: {\n    fetchUserById() {\n      getInfoByName(this.$route.params.username, this.page.current, this.page.size).then((res) => {\n        const { data } = res\n        this.topicUser = data.user\n        this.page.current = data.topics.current\n        this.page.size = data.topics.size\n        this.page.total = data.topics.total\n        this.topics = data.topics.records\n      })\n    },\n    handleDelete(id) {\n      deleteTopic(id).then(value => {\n        const { code, message } = value\n        alert(message)\n\n        if (code === 200) {\n          setTimeout(() => {\n            this.$router.push({ path: '/' })\n          }, 500)\n        }\n      })\n    }\n  }\n}\n</script>\n\n<style scoped>\n\n</style>\n"
  },
  {
    "path": "src/views/user/Setting.vue",
    "content": "<template>\n  <section>\n    <el-card shadow=\"never\">\n      <div slot=\"header\">\n        个人设置\n      </div>\n      <div class=\"columns\">\n        <div class=\"column is-full\">\n          <el-tabs v-model=\"activeName\" @tab-click=\"handleClick\">\n            <el-tab-pane label=\"基础信息\" name=\"first\">\n              <el-form :label-position=\"labelPosition\" label-width=\"100px\" :model=\"user\">\n                <el-form-item label=\"账号\">\n                  <el-input v-model=\"user.username\" disabled />\n                </el-form-item>\n                <el-form-item label=\"昵称\">\n                  <el-input v-model=\"user.alias\" />\n                </el-form-item>\n                <el-form-item label=\"简介\">\n                  <el-input v-model=\"user.bio\" />\n                </el-form-item>\n                <el-form-item>\n                  <el-button type=\"primary\" @click=\"submitForm('ruleForm')\">提交</el-button>\n                  <el-button @click=\"resetForm('ruleForm')\">重置</el-button>\n                </el-form-item>\n              </el-form>\n            </el-tab-pane>\n            <el-tab-pane label=\"头像\" name=\"second\">\n              <figure class=\"image is-48x48\">\n                <img :src=\"`https://cn.gravatar.com/avatar/${this.user.id}?s=164&d=monsterid`\">\n              </figure>\n            </el-tab-pane>\n            <el-tab-pane label=\"电子邮箱\" name=\"third\">\n              <el-form ref=\"dynamicValidateForm\" :model=\"user\" label-width=\"100px\" class=\"demo-dynamic\">\n                <el-form-item\n                  prop=\"email\"\n                  label=\"邮箱\"\n                  :rules=\"[\n                    { required: true, message: '请输入邮箱地址', trigger: 'blur' },\n                    { type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }\n                  ]\"\n                >\n                  <el-input v-model=\"user.email\" />\n                </el-form-item>\n\n                <el-form-item>\n                  <el-button type=\"primary\" @click=\"submitForm('dynamicValidateForm')\">提交</el-button>\n                  <el-button @click=\"resetForm('dynamicValidateForm')\">重置</el-button>\n                </el-form-item>\n              </el-form>\n            </el-tab-pane>\n            <el-tab-pane label=\"手机号\" name=\"fourth\">\n              <el-form ref=\"dynamicValidateForm\" :model=\"user\" label-width=\"100px\" class=\"demo-dynamic\">\n                <el-form-item>\n                  <el-input v-model=\"user.mobile\" />\n                </el-form-item>\n\n                <el-form-item>\n                  <el-button type=\"primary\" @click=\"submitForm('dynamicValidateForm')\">提交</el-button>\n                  <el-button @click=\"resetForm('dynamicValidateForm')\">重置</el-button>\n                </el-form-item>\n              </el-form>\n            </el-tab-pane>\n          </el-tabs>\n        </div>\n      </div>\n    </el-card>\n  </section>\n</template>\n\n<script>\nimport {getInfo, update} from '@/api/user'\n\nexport default {\n  name: 'Setting',\n  data() {\n    return {\n      activeName: 'first',\n      labelPosition: 'right',\n      user: {\n        id: '',\n        username: '',\n        alias: '',\n        bio: '',\n        email: '',\n        mobile: '',\n        avatar: ''\n      }\n    }\n  },\n  created() {\n    this.fetchInfo()\n  },\n  methods: {\n    fetchInfo() {\n      getInfo(this.$route.params.username).then(res => {\n        console.log(res)\n        const { data } = res\n        this.user = data\n      })\n    },\n    handleClick(tab, event) {\n      console.log(tab, event)\n    },\n    submitForm(formName) {\n      console.log(this.user)\n      update(this.user).then(res => {\n        this.$message.success('信息修改成功')\n        this.fetchInfo()\n      })\n    },\n    resetForm(formName) {\n      this.$refs[formName].resetFields()\n    }\n  }\n}\n</script>\n\n<style scoped>\n\n</style>\n"
  }
]