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