├── .gitattributes ├── LICENSE ├── README.md ├── blogserver ├── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── sang │ │ ├── BlogserverApplication.java │ │ ├── bean │ │ ├── Article.java │ │ ├── Category.java │ │ ├── RespBean.java │ │ ├── Role.java │ │ ├── Tags.java │ │ └── User.java │ │ ├── config │ │ ├── AuthenticationAccessDeniedHandler.java │ │ └── WebSecurityConfig.java │ │ ├── controller │ │ ├── ArticleController.java │ │ ├── CategoryController.java │ │ ├── LoginRegController.java │ │ ├── UserController.java │ │ └── admin │ │ │ ├── AdminController.java │ │ │ └── UserManaController.java │ │ ├── mapper │ │ ├── ArticleMapper.java │ │ ├── ArticleMapper.xml │ │ ├── CategoryMapper.java │ │ ├── CategoryMapper.xml │ │ ├── RolesMapper.java │ │ ├── RolesMapper.xml │ │ ├── TagsMapper.java │ │ ├── TagsMapper.xml │ │ ├── UserMapper.java │ │ └── UserMapper.xml │ │ ├── service │ │ ├── ArticleService.java │ │ ├── CategoryService.java │ │ ├── DataStatisticsComponent.java │ │ └── UserService.java │ │ └── utils │ │ ├── DateTypeHandler.java │ │ └── Util.java │ └── resources │ ├── application.properties │ ├── log4j.properties │ ├── mybatis-config.xml │ ├── static │ ├── index.html │ └── static │ │ ├── css │ │ ├── app.dcf53770d721d7571b903ad1173be0fa.css │ │ └── app.dcf53770d721d7571b903ad1173be0fa.css.map │ │ ├── fonts │ │ ├── element-icons.6f0a763.ttf │ │ ├── fontawesome-webfont.674f50d.eot │ │ ├── fontawesome-webfont.af7ae50.woff2 │ │ ├── fontawesome-webfont.b06871f.ttf │ │ ├── fontawesome-webfont.fee66e7.woff │ │ ├── fontello.068ca2b.ttf │ │ └── fontello.e73a064.eot │ │ ├── img │ │ ├── fontawesome-webfont.912ec66.svg │ │ └── fontello.9354499.svg │ │ └── js │ │ ├── app.ca4cf1cc4fa16a3105b3.js │ │ ├── app.ca4cf1cc4fa16a3105b3.js.map │ │ ├── manifest.35480ac00baae1b754e6.js │ │ ├── manifest.35480ac00baae1b754e6.js.map │ │ ├── vendor.dd31c3932b33cfeec940.js │ │ └── vendor.dd31c3932b33cfeec940.js.map │ └── templates │ └── 01.html ├── docs ├── article.png ├── category.png ├── datastatistics.png ├── db │ └── vueblog.sql ├── login.png ├── post.png ├── qrcode.jpg ├── usermana.png └── vblog-database-er.png └── vueblog ├── .babelrc ├── .editorconfig ├── .gitignore ├── .postcssrc.js ├── build ├── build.js ├── check-versions.js ├── logo.png ├── utils.js ├── vue-loader.conf.js ├── webpack.base.conf.js ├── webpack.dev.conf.js └── webpack.prod.conf.js ├── config ├── dev.env.js ├── index.js └── prod.env.js ├── index.html ├── package-lock.json ├── package.json ├── src ├── App.vue ├── assets │ └── logo.png ├── components │ ├── ArticleList.vue │ ├── BlogCfg.vue │ ├── BlogDetail.vue │ ├── BlogTable.vue │ ├── CateMana.vue │ ├── DataCharts.vue │ ├── Home.bak.vue │ ├── Home.vue │ ├── Login.vue │ ├── PostArticle.vue │ └── UserMana.vue ├── main.js ├── router │ └── index.js ├── styles │ └── element-variables.scss └── utils │ ├── api.js │ ├── filter_utils.js │ └── utils.js └── static └── .gitkeep /.gitattributes: -------------------------------------------------------------------------------- 1 | *.css linguist-language=java 2 | *.less linguist-language=java 3 | *.js linguist-language=java 4 | *.html linguist-language=java 5 | *.sql linguist-language=java 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 王松 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ✌️📔 一个采用前后端分离开发模式实现的多用户博客管理平台 2 | 3 | > 项目在线演示地址 : http://vblog.itboyhub.com/ 🔐用户名 : sang,密码 : 123 ( 更多信息请参考数据库,密码都是123啦~ ) 4 | 5 | 6 | ### 项目简介 7 | ✨*这或许是最简单的 springboot + vue 实现的前后端分离项目啦 ! 非常适合作为初次学习前后端开发模式的同学第一个入门项目哟~* 8 | 9 | 10 | ### 开发环境 11 | 12 | | 工具 | 版本或描述 | 13 | | ------- | ----------------------------- | 14 | | `OS` | Windows 10 | 15 | | `JDK` | 1.8+ | 16 | | `IDE` | IntelliJ IDEA 2019.1 | 17 | | `Maven` | 3.6.0 | 18 | | `MySQL` | 8.0.0+ | 19 | 20 | 21 | 22 | ### 项目模块划分 23 | 24 | | 模块 | 释义 | 25 | | ------------- | ------------------------------- | 26 | | `blogserver` | 后台管理模块 | 27 | | `vueblog` | 前台管理模块 | 28 | | `docs` | 文档( 存储数据库文件及截图 ) | 29 | 30 | 31 | 32 | ### 数据库模型 33 | ![sql model](https://raw.githubusercontent.com/YUbuntu0109/VBlog/newfun-restore-article-del-m/docs/vblog-database-er.png) 34 | 35 | 36 | 37 | ### 图片预览 38 | *:camera_flash: 用户登录页* 39 | ![登录](https://raw.githubusercontent.com/lenve/VBlog/master/doc/login.png) 40 | 41 | *:camera_flash: 文章列表页* 42 | ![文章列表](https://raw.githubusercontent.com/lenve/VBlog/master/doc/article.png) 43 | 44 | *:camera_flash: 发表文章页* 45 | ![发表文章](https://raw.githubusercontent.com/lenve/VBlog/master/doc/post.png) 46 | 47 | *:camera_flash: 用户管理页* 48 | ![用户管理](https://raw.githubusercontent.com/lenve/VBlog/master/doc/usermana.png) 49 | 50 | *:camera_flash: 栏目管理页* 51 | ![栏目管理](https://raw.githubusercontent.com/lenve/VBlog/master/doc/category.png) 52 | 53 | *:camera_flash: 数据统计页* 54 | ![数据统计](https://raw.githubusercontent.com/lenve/VBlog/master/doc/datastatistics.png) 55 | 56 | 57 | 58 | ### 项目技术栈 59 | 60 | | 后端技术栈 | 前端技术栈 | 61 | | ------------------------- | ---------------------- | 62 | | `SpringBoot` | `Vue` | 63 | | `SpringSecurity` | `axios` | 64 | | `MyBatis` | `ElementUI` | 65 | | `Druid` | `vue-echarts` | 66 | | `MySQL` | `mavon-editor` | 67 | | 部分接口遵循`Restful`风格 | `vue-router` | 68 | 69 | > 其它一些琐碎的技术这里就不一一列举了咯~ 70 | 71 | 72 | 73 | ### 使用说明 74 | 1. *将本项目中的`blogserver`( 后端管理模块 )导入到`IDE`* 75 | 2. *导入数据库`docs/db/vueblog.sql`* 76 | 3. *修改`resources/application.properties`配置文件* 77 | 4. *运行项目( 三种方式 )* 78 | 1. 项目根目录下执行`mvn -X clean package -Dmaven.test.skip=true`编译打包,然后执行`java -jar blogserver/target/blogserver.jar` 79 | 2. 项目根目录下执行`mvn springboot:run` 80 | 3. 直接运行`BlogserverApplication.java` 81 | 5. *浏览器访问`http://localhost:8081/index.html`* 82 | 83 | > 注意 : 如果要做二次开发,请继续看第五、六步哟 ! 84 | 85 | 6. *进入`vueblog`( 前端管理模块 )目录中,在命令行依次执行如下命令 :* 86 | ``` 87 | # 安装依赖 88 | npm install 89 | 90 | # 在 localhost:8080 启动项目 91 | npm run dev 92 | ``` 93 | 94 | > 由于我在`vueblog`( 前端管理模块 )项目中已经配置了端口转发,既将数据转发到`SpringBoot`上,因此项目启动之后,在浏览器中输入`http://localhost:8080`就可以访问我们的前端项目啦~ 所有的请求将通过端口转发将数据传输到`SpringBoot`中( 注意 : 这个过程要保持`SpringBoot`项目,既`blogserver`后台管理模块是处于运行状态的 ! ) 95 | 96 | 7. *最后可以使用`WebStorm`等前端开发工具打开`vueblog`项目( 前端管理模块 )继续开发,待开发完成后,当项目要上线时,依然进入到`vueblog`目录,然后执行如下命令 :* 97 | ``` 98 | npm run build 99 | ``` 100 | 101 | *该命令执行成功之后,`vueblog`目录下将生成一个名为`dist`的文件夹,然后将该文件夹中的`static`文件夹及`index.html`文件拷贝到`SpringBoot`项目中的`resources/static/`目录下,然后就可以像第 `4` 步那样通过启动后端管理模块来访问该项目咯~* 102 | 103 | > 步骤 `6` 中需要大家对`NodeJS`、`NPM`等有一定的使用经验,不熟悉的小伙伴可以先自行学习下 : [Vue官方教程](https://cn.vuejs.org/v2/guide/) 104 | 105 | 106 | 107 | ### 项目依赖 108 | 1. [`vue-echarts`](https://github.com/Justineo/vue-echarts) 109 | 2. [`mavonEditor`](https://github.com/hinesboy/mavonEditor) 110 | -------------------------------------------------------------------------------- /blogserver/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ 26 | /src/main/resources/application-prod.yml -------------------------------------------------------------------------------- /blogserver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.sang 8 | blogserver 9 | 0.0.1-SNAPSHOT 10 | jar 11 | 12 | blogserver 13 | Vue博客服务端 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-parent 19 | 1.5.9.RELEASE 20 | 21 | 22 | 23 | 24 | UTF-8 25 | UTF-8 26 | 1.8 27 | 28 | 29 | 30 | 31 | org.mybatis.spring.boot 32 | mybatis-spring-boot-starter 33 | 1.3.1 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-security 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-web 42 | 43 | 44 | commons-codec 45 | commons-codec 46 | 1.11 47 | 48 | 49 | mysql 50 | mysql-connector-java 51 | 8.0.15 52 | 53 | 54 | commons-io 55 | commons-io 56 | 2.5 57 | 58 | 59 | com.alibaba 60 | druid 61 | 1.0.29 62 | 63 | 64 | aopalliance 65 | aopalliance 66 | 1.0 67 | 68 | 69 | 70 | 71 | 72 | 73 | src/main/java 74 | 75 | **/*.xml 76 | 77 | 78 | 79 | src/main/resources 80 | 81 | 82 | 83 | 84 | org.springframework.boot 85 | spring-boot-maven-plugin 86 | 87 | true 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/BlogserverApplication.java: -------------------------------------------------------------------------------- 1 | package org.sang; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.scheduling.annotation.EnableScheduling; 6 | 7 | @SpringBootApplication 8 | @EnableScheduling//开启定时任务支持 9 | public class BlogserverApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(BlogserverApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/bean/Article.java: -------------------------------------------------------------------------------- 1 | package org.sang.bean; 2 | 3 | import java.sql.Timestamp; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by sang on 2017/12/20. 8 | */ 9 | public class Article { 10 | private Long id; 11 | private String title; 12 | private String mdContent; 13 | private String htmlContent; 14 | private String summary; 15 | private Long cid; 16 | private Long uid; 17 | private Timestamp publishDate; 18 | private Integer state; 19 | private Integer pageView; 20 | private Timestamp editTime; 21 | private String[] dynamicTags; 22 | private String nickname; 23 | private String cateName; 24 | private List tags; 25 | private String stateStr; 26 | 27 | public String getStateStr() { 28 | return stateStr; 29 | } 30 | 31 | public void setStateStr(String stateStr) { 32 | this.stateStr = stateStr; 33 | } 34 | 35 | public List getTags() { 36 | return tags; 37 | } 38 | 39 | public void setTags(List tags) { 40 | this.tags = tags; 41 | } 42 | 43 | public String getNickname() { 44 | return nickname; 45 | } 46 | 47 | public void setNickname(String nickname) { 48 | this.nickname = nickname; 49 | } 50 | 51 | public String getCateName() { 52 | return cateName; 53 | } 54 | 55 | public void setCateName(String cateName) { 56 | this.cateName = cateName; 57 | } 58 | 59 | public String[] getDynamicTags() { 60 | return dynamicTags; 61 | } 62 | 63 | public void setDynamicTags(String[] dynamicTags) { 64 | this.dynamicTags = dynamicTags; 65 | } 66 | 67 | public Timestamp getEditTime() { 68 | return editTime; 69 | } 70 | 71 | public void setEditTime(Timestamp editTime) { 72 | this.editTime = editTime; 73 | } 74 | 75 | public Long getId() { 76 | return id; 77 | } 78 | 79 | public void setId(Long id) { 80 | this.id = id; 81 | } 82 | 83 | public String getTitle() { 84 | return title; 85 | } 86 | 87 | public void setTitle(String title) { 88 | this.title = title; 89 | } 90 | 91 | public String getMdContent() { 92 | return mdContent; 93 | } 94 | 95 | public void setMdContent(String mdContent) { 96 | this.mdContent = mdContent; 97 | } 98 | 99 | public String getHtmlContent() { 100 | return htmlContent; 101 | } 102 | 103 | public void setHtmlContent(String htmlContent) { 104 | this.htmlContent = htmlContent; 105 | } 106 | 107 | public String getSummary() { 108 | return summary; 109 | } 110 | 111 | public void setSummary(String summary) { 112 | this.summary = summary; 113 | } 114 | 115 | public Long getCid() { 116 | return cid; 117 | } 118 | 119 | public void setCid(Long cid) { 120 | this.cid = cid; 121 | } 122 | 123 | public Long getUid() { 124 | return uid; 125 | } 126 | 127 | public void setUid(Long uid) { 128 | this.uid = uid; 129 | } 130 | 131 | public Timestamp getPublishDate() { 132 | return publishDate; 133 | } 134 | 135 | public void setPublishDate(Timestamp publishDate) { 136 | this.publishDate = publishDate; 137 | } 138 | 139 | public Integer getState() { 140 | return state; 141 | } 142 | 143 | public void setState(Integer state) { 144 | this.state = state; 145 | } 146 | 147 | public Integer getPageView() { 148 | return pageView; 149 | } 150 | 151 | public void setPageView(Integer pageView) { 152 | this.pageView = pageView; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/bean/Category.java: -------------------------------------------------------------------------------- 1 | package org.sang.bean; 2 | 3 | import java.sql.Timestamp; 4 | 5 | /** 6 | * Created by sang on 2017/12/19. 7 | */ 8 | public class Category { 9 | private Long id; 10 | private String cateName; 11 | private Timestamp date; 12 | 13 | public Category() { 14 | } 15 | 16 | public Timestamp getDate() { 17 | return date; 18 | } 19 | 20 | public void setDate(Timestamp date) { 21 | this.date = date; 22 | } 23 | 24 | public Long getId() { 25 | return id; 26 | } 27 | 28 | public void setId(Long id) { 29 | this.id = id; 30 | } 31 | 32 | public String getCateName() { 33 | return cateName; 34 | } 35 | 36 | public void setCateName(String cateName) { 37 | this.cateName = cateName; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/bean/RespBean.java: -------------------------------------------------------------------------------- 1 | package org.sang.bean; 2 | 3 | /** 4 | * Created by sang on 2017/12/17. 5 | */ 6 | public class RespBean { 7 | private String status; 8 | private String msg; 9 | 10 | public RespBean() { 11 | } 12 | 13 | public RespBean(String status, String msg) { 14 | 15 | this.status = status; 16 | this.msg = msg; 17 | } 18 | 19 | public String getStatus() { 20 | return status; 21 | } 22 | 23 | public void setStatus(String status) { 24 | this.status = status; 25 | } 26 | 27 | public String getMsg() { 28 | return msg; 29 | } 30 | 31 | public void setMsg(String msg) { 32 | this.msg = msg; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/bean/Role.java: -------------------------------------------------------------------------------- 1 | package org.sang.bean; 2 | 3 | /** 4 | * Created by sang on 2017/12/17. 5 | */ 6 | public class Role { 7 | private Long id; 8 | private String name; 9 | 10 | public Role() { 11 | } 12 | 13 | public Long getId() { 14 | 15 | return id; 16 | } 17 | 18 | public void setId(Long id) { 19 | this.id = id; 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public void setName(String name) { 27 | this.name = name; 28 | } 29 | 30 | public Role(Long id, String name) { 31 | 32 | this.id = id; 33 | this.name = name; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/bean/Tags.java: -------------------------------------------------------------------------------- 1 | package org.sang.bean; 2 | 3 | /** 4 | * Created by sang on 2017/12/21. 5 | */ 6 | public class Tags { 7 | private Long id; 8 | private String tagName; 9 | 10 | public Long getId() { 11 | return id; 12 | } 13 | 14 | public void setId(Long id) { 15 | this.id = id; 16 | } 17 | 18 | public String getTagName() { 19 | return tagName; 20 | } 21 | 22 | public void setTagName(String tagName) { 23 | this.tagName = tagName; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/bean/User.java: -------------------------------------------------------------------------------- 1 | package org.sang.bean; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | 8 | import java.sql.Timestamp; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by sang on 2017/12/17. 14 | */ 15 | public class User implements UserDetails { 16 | private Long id; 17 | private String username; 18 | private String password; 19 | private String nickname; 20 | private boolean enabled; 21 | private List roles; 22 | private String email; 23 | private String userface; 24 | private Timestamp regTime; 25 | 26 | public Timestamp getRegTime() { 27 | return regTime; 28 | } 29 | 30 | public void setRegTime(Timestamp regTime) { 31 | this.regTime = regTime; 32 | } 33 | 34 | public String getUserface() { 35 | return userface; 36 | } 37 | 38 | public void setUserface(String userface) { 39 | this.userface = userface; 40 | } 41 | 42 | public String getEmail() { 43 | return email; 44 | } 45 | 46 | public void setEmail(String email) { 47 | this.email = email; 48 | } 49 | 50 | public List getRoles() { 51 | return roles; 52 | } 53 | 54 | public void setRoles(List roles) { 55 | this.roles = roles; 56 | } 57 | 58 | public Long getId() { 59 | return id; 60 | } 61 | 62 | public void setId(Long id) { 63 | this.id = id; 64 | } 65 | 66 | public String getUsername() { 67 | return username; 68 | } 69 | 70 | @Override 71 | @JsonIgnore 72 | public boolean isAccountNonExpired() { 73 | return true; 74 | } 75 | 76 | @Override 77 | @JsonIgnore 78 | public boolean isAccountNonLocked() { 79 | return true; 80 | } 81 | 82 | @Override 83 | @JsonIgnore 84 | public boolean isCredentialsNonExpired() { 85 | return true; 86 | } 87 | 88 | @Override 89 | public boolean isEnabled() { 90 | return enabled; 91 | } 92 | 93 | public void setEnabled(boolean enabled) { 94 | this.enabled = enabled; 95 | } 96 | 97 | @Override 98 | @JsonIgnore 99 | public List getAuthorities() { 100 | List authorities = new ArrayList<>(); 101 | for (Role role : roles) { 102 | authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName())); 103 | } 104 | return authorities; 105 | } 106 | 107 | public void setUsername(String username) { 108 | this.username = username; 109 | } 110 | 111 | 112 | public String getPassword() { 113 | return password; 114 | } 115 | 116 | public void setPassword(String password) { 117 | this.password = password; 118 | } 119 | 120 | public String getNickname() { 121 | return nickname; 122 | } 123 | 124 | public void setNickname(String nickname) { 125 | this.nickname = nickname; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/config/AuthenticationAccessDeniedHandler.java: -------------------------------------------------------------------------------- 1 | package org.sang.config; 2 | 3 | import org.springframework.security.access.AccessDeniedException; 4 | import org.springframework.security.web.access.AccessDeniedHandler; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | import java.io.PrintWriter; 11 | 12 | /** 13 | * Created by sang on 2017/12/22. 14 | */ 15 | public class AuthenticationAccessDeniedHandler implements AccessDeniedHandler { 16 | @Override 17 | public void handle(HttpServletRequest httpServletRequest, HttpServletResponse resp, AccessDeniedException e) throws IOException, ServletException { 18 | resp.setStatus(HttpServletResponse.SC_FORBIDDEN); 19 | resp.setCharacterEncoding("UTF-8"); 20 | PrintWriter out = resp.getWriter(); 21 | out.write("权限不足,请联系管理员!"); 22 | out.flush(); 23 | out.close(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/config/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package org.sang.config; 2 | 3 | import org.sang.service.UserService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 11 | import org.springframework.security.core.Authentication; 12 | import org.springframework.security.core.AuthenticationException; 13 | import org.springframework.security.crypto.password.PasswordEncoder; 14 | import org.springframework.security.web.access.AccessDeniedHandler; 15 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; 16 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 17 | import org.springframework.util.DigestUtils; 18 | 19 | import javax.servlet.ServletException; 20 | import javax.servlet.http.HttpServletRequest; 21 | import javax.servlet.http.HttpServletResponse; 22 | import java.io.IOException; 23 | import java.io.PrintWriter; 24 | 25 | /** 26 | * Created by sang on 2017/12/17. 27 | */ 28 | @Configuration 29 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 30 | @Autowired 31 | UserService userService; 32 | 33 | @Override 34 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 35 | auth.userDetailsService(userService).passwordEncoder(new PasswordEncoder() { 36 | @Override 37 | public String encode(CharSequence charSequence) { 38 | return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes()); 39 | } 40 | 41 | /** 42 | * @param charSequence 明文 43 | * @param s 密文 44 | * @return 45 | */ 46 | @Override 47 | public boolean matches(CharSequence charSequence, String s) { 48 | return s.equals(DigestUtils.md5DigestAsHex(charSequence.toString().getBytes())); 49 | } 50 | }); 51 | } 52 | 53 | @Override 54 | protected void configure(HttpSecurity http) throws Exception { 55 | http.authorizeRequests() 56 | .antMatchers("/admin/category/all").authenticated() 57 | .antMatchers("/admin/**","/reg").hasRole("超级管理员")///admin/**的URL都需要有超级管理员角色,如果使用.hasAuthority()方法来配置,需要在参数中加上ROLE_,如下.hasAuthority("ROLE_超级管理员") 58 | .anyRequest().authenticated()//其他的路径都是登录后即可访问 59 | .and().formLogin().loginPage("/login_page").successHandler(new AuthenticationSuccessHandler() { 60 | @Override 61 | public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { 62 | httpServletResponse.setContentType("application/json;charset=utf-8"); 63 | PrintWriter out = httpServletResponse.getWriter(); 64 | out.write("{\"status\":\"success\",\"msg\":\"登录成功\"}"); 65 | out.flush(); 66 | out.close(); 67 | } 68 | }) 69 | .failureHandler(new AuthenticationFailureHandler() { 70 | @Override 71 | public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { 72 | httpServletResponse.setContentType("application/json;charset=utf-8"); 73 | PrintWriter out = httpServletResponse.getWriter(); 74 | out.write("{\"status\":\"error\",\"msg\":\"登录失败\"}"); 75 | out.flush(); 76 | out.close(); 77 | } 78 | }).loginProcessingUrl("/login") 79 | .usernameParameter("username").passwordParameter("password").permitAll() 80 | .and().logout().permitAll().and().csrf().disable().exceptionHandling().accessDeniedHandler(getAccessDeniedHandler()); 81 | } 82 | 83 | @Override 84 | public void configure(WebSecurity web) throws Exception { 85 | web.ignoring().antMatchers("/blogimg/**","/index.html","/static/**"); 86 | } 87 | 88 | @Bean 89 | AccessDeniedHandler getAccessDeniedHandler() { 90 | return new AuthenticationAccessDeniedHandler(); 91 | } 92 | } -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/controller/ArticleController.java: -------------------------------------------------------------------------------- 1 | package org.sang.controller; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | import org.sang.bean.Article; 5 | import org.sang.bean.RespBean; 6 | import org.sang.service.ArticleService; 7 | import org.sang.utils.Util; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.bind.annotation.*; 10 | import org.springframework.web.multipart.MultipartFile; 11 | 12 | import javax.servlet.http.HttpServletRequest; 13 | import java.io.File; 14 | import java.io.FileOutputStream; 15 | import java.io.IOException; 16 | import java.text.SimpleDateFormat; 17 | import java.util.*; 18 | 19 | /** 20 | * Created by sang on 2017/12/20. 21 | */ 22 | @RestController 23 | @RequestMapping("/article") 24 | public class ArticleController { 25 | 26 | private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); 27 | 28 | @Autowired 29 | ArticleService articleService; 30 | 31 | @RequestMapping(value = "/", method = RequestMethod.POST) 32 | public RespBean addNewArticle(Article article) { 33 | int result = articleService.addNewArticle(article); 34 | if (result == 1) { 35 | return new RespBean("success", article.getId() + ""); 36 | } else { 37 | return new RespBean("error", article.getState() == 0 ? "文章保存失败!" : "文章发表失败!"); 38 | } 39 | } 40 | 41 | /** 42 | * 上传图片 43 | * 44 | * @return 返回值为图片的地址 45 | */ 46 | @RequestMapping(value = "/uploadimg", method = RequestMethod.POST) 47 | public RespBean uploadImg(HttpServletRequest req, MultipartFile image) { 48 | StringBuffer url = new StringBuffer(); 49 | String filePath = "/blogimg/" + sdf.format(new Date()); 50 | String imgFolderPath = req.getServletContext().getRealPath(filePath); 51 | File imgFolder = new File(imgFolderPath); 52 | if (!imgFolder.exists()) { 53 | imgFolder.mkdirs(); 54 | } 55 | url.append(req.getScheme()) 56 | .append("://") 57 | .append(req.getServerName()) 58 | .append(":") 59 | .append(req.getServerPort()) 60 | .append(req.getContextPath()) 61 | .append(filePath); 62 | String imgName = UUID.randomUUID() + "_" + image.getOriginalFilename().replaceAll(" ", ""); 63 | try { 64 | IOUtils.write(image.getBytes(), new FileOutputStream(new File(imgFolder, imgName))); 65 | url.append("/").append(imgName); 66 | return new RespBean("success", url.toString()); 67 | } catch (IOException e) { 68 | e.printStackTrace(); 69 | } 70 | return new RespBean("error", "上传失败!"); 71 | } 72 | 73 | @RequestMapping(value = "/all", method = RequestMethod.GET) 74 | public Map getArticleByState(@RequestParam(value = "state", defaultValue = "-1") Integer state, @RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "count", defaultValue = "6") Integer count,String keywords) { 75 | int totalCount = articleService.getArticleCountByState(state, Util.getCurrentUser().getId(),keywords); 76 | List
articles = articleService.getArticleByState(state, page, count,keywords); 77 | Map map = new HashMap<>(); 78 | map.put("totalCount", totalCount); 79 | map.put("articles", articles); 80 | return map; 81 | } 82 | 83 | @RequestMapping(value = "/{aid}", method = RequestMethod.GET) 84 | public Article getArticleById(@PathVariable Long aid) { 85 | return articleService.getArticleById(aid); 86 | } 87 | 88 | @RequestMapping(value = "/dustbin", method = RequestMethod.PUT) 89 | public RespBean updateArticleState(Long[] aids, Integer state) { 90 | if (articleService.updateArticleState(aids, state) == aids.length) { 91 | return new RespBean("success", "删除成功!"); 92 | } 93 | return new RespBean("error", "删除失败!"); 94 | } 95 | 96 | @RequestMapping(value = "/restore", method = RequestMethod.PUT) 97 | public RespBean restoreArticle(Integer articleId) { 98 | if (articleService.restoreArticle(articleId) == 1) { 99 | return new RespBean("success", "还原成功!"); 100 | } 101 | return new RespBean("error", "还原失败!"); 102 | } 103 | 104 | @RequestMapping("/dataStatistics") 105 | public Map dataStatistics() { 106 | Map map = new HashMap<>(); 107 | List categories = articleService.getCategories(); 108 | List dataStatistics = articleService.getDataStatistics(); 109 | map.put("categories", categories); 110 | map.put("ds", dataStatistics); 111 | return map; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/controller/CategoryController.java: -------------------------------------------------------------------------------- 1 | package org.sang.controller; 2 | 3 | import org.sang.bean.Category; 4 | import org.sang.bean.RespBean; 5 | import org.sang.service.CategoryService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestMethod; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * 超级管理员专属Controller 16 | */ 17 | @RestController 18 | @RequestMapping("/admin/category") 19 | public class CategoryController { 20 | @Autowired 21 | CategoryService categoryService; 22 | 23 | @RequestMapping(value = "/all", method = RequestMethod.GET) 24 | public List getAllCategories() { 25 | return categoryService.getAllCategories(); 26 | } 27 | 28 | @RequestMapping(value = "/{ids}", method = RequestMethod.DELETE) 29 | public RespBean deleteById(@PathVariable String ids) { 30 | boolean result = categoryService.deleteCategoryByIds(ids); 31 | if (result) { 32 | return new RespBean("success", "删除成功!"); 33 | } 34 | return new RespBean("error", "删除失败!"); 35 | } 36 | 37 | @RequestMapping(value = "/", method = RequestMethod.POST) 38 | public RespBean addNewCate(Category category) { 39 | 40 | if ("".equals(category.getCateName()) || category.getCateName() == null) { 41 | return new RespBean("error", "请输入栏目名称!"); 42 | } 43 | 44 | int result = categoryService.addCategory(category); 45 | 46 | if (result == 1) { 47 | return new RespBean("success", "添加成功!"); 48 | } 49 | return new RespBean("error", "添加失败!"); 50 | } 51 | 52 | @RequestMapping(value = "/", method = RequestMethod.PUT) 53 | public RespBean updateCate(Category category) { 54 | int i = categoryService.updateCategoryById(category); 55 | if (i == 1) { 56 | return new RespBean("success", "修改成功!"); 57 | } 58 | return new RespBean("error", "修改失败!"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/controller/LoginRegController.java: -------------------------------------------------------------------------------- 1 | package org.sang.controller; 2 | 3 | import org.sang.bean.RespBean; 4 | import org.sang.bean.User; 5 | import org.sang.service.UserService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | /** 11 | * Created by sang on 2017/12/17. 12 | */ 13 | @RestController 14 | public class LoginRegController { 15 | 16 | @Autowired 17 | UserService userService; 18 | 19 | @RequestMapping("/login_error") 20 | public RespBean loginError() { 21 | return new RespBean("error", "登录失败!"); 22 | } 23 | 24 | @RequestMapping("/login_success") 25 | public RespBean loginSuccess() { 26 | return new RespBean("success", "登录成功!"); 27 | } 28 | 29 | /** 30 | * 如果自动跳转到这个页面,说明用户未登录,返回相应的提示即可 31 | *

32 | * 如果要支持表单登录,可以在这个方法中判断请求的类型,进而决定返回JSON还是HTML页面 33 | * 34 | * @return 35 | */ 36 | @RequestMapping("/login_page") 37 | public RespBean loginPage() { 38 | return new RespBean("error", "尚未登录,请登录!"); 39 | } 40 | 41 | @RequestMapping("/reg") 42 | public RespBean reg(User user) { 43 | int result = userService.reg(user); 44 | if (result == 0) { 45 | //成功 46 | return new RespBean("success", "注册成功!"); 47 | } else if (result == 1) { 48 | return new RespBean("error", "用户名重复,注册失败!"); 49 | } else { 50 | //失败 51 | return new RespBean("error", "注册失败!"); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package org.sang.controller; 2 | 3 | import org.sang.bean.RespBean; 4 | import org.sang.service.UserService; 5 | import org.sang.utils.Util; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.core.GrantedAuthority; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestMethod; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * Created by sang on 2017/12/24. 16 | */ 17 | @RestController 18 | public class UserController { 19 | 20 | @Autowired 21 | UserService userService; 22 | 23 | @RequestMapping("/currentUserName") 24 | public String currentUserName() { 25 | return Util.getCurrentUser().getNickname(); 26 | } 27 | 28 | @RequestMapping("/currentUserId") 29 | public Long currentUserId() { 30 | return Util.getCurrentUser().getId(); 31 | } 32 | 33 | @RequestMapping("/currentUserEmail") 34 | public String currentUserEmail() { 35 | return Util.getCurrentUser().getEmail(); 36 | } 37 | 38 | @RequestMapping("/isAdmin") 39 | public Boolean isAdmin() { 40 | List authorities = Util.getCurrentUser().getAuthorities(); 41 | for (GrantedAuthority authority : authorities) { 42 | if (authority.getAuthority().contains("超级管理员")) { 43 | return true; 44 | } 45 | } 46 | return false; 47 | } 48 | 49 | @RequestMapping(value = "/updateUserEmail",method = RequestMethod.PUT) 50 | public RespBean updateUserEmail(String email) { 51 | if (userService.updateUserEmail(email) == 1) { 52 | return new RespBean("success", "开启成功!"); 53 | } 54 | return new RespBean("error", "开启失败!"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/controller/admin/AdminController.java: -------------------------------------------------------------------------------- 1 | package org.sang.controller.admin; 2 | 3 | import org.sang.bean.Article; 4 | import org.sang.bean.RespBean; 5 | import org.sang.service.ArticleService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestMethod; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | /** 17 | * 超级管理员专属Controller 18 | */ 19 | @RestController 20 | @RequestMapping("/admin") 21 | public class AdminController { 22 | @Autowired 23 | ArticleService articleService; 24 | 25 | @RequestMapping(value = "/article/all", method = RequestMethod.GET) 26 | public Map getArticleByStateByAdmin(@RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "count", defaultValue = "6") Integer count, String keywords) { 27 | List

articles = articleService.getArticleByState(-2, page, count, keywords); 28 | Map map = new HashMap<>(); 29 | map.put("articles", articles); 30 | map.put("totalCount", articleService.getArticleCountByState(1, null, keywords)); 31 | return map; 32 | } 33 | 34 | @RequestMapping(value = "/article/dustbin", method = RequestMethod.PUT) 35 | public RespBean updateArticleState(Long[] aids, Integer state) { 36 | if (articleService.updateArticleState(aids, state) == aids.length) { 37 | return new RespBean("success", "删除成功!"); 38 | } 39 | return new RespBean("error", "删除失败!"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/controller/admin/UserManaController.java: -------------------------------------------------------------------------------- 1 | package org.sang.controller.admin; 2 | 3 | import org.sang.bean.RespBean; 4 | import org.sang.bean.Role; 5 | import org.sang.bean.User; 6 | import org.sang.service.UserService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RequestMethod; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * Created by sang on 2017/12/24. 17 | */ 18 | @RestController 19 | @RequestMapping("/admin") 20 | public class UserManaController { 21 | @Autowired 22 | UserService userService; 23 | 24 | @RequestMapping(value = "/user", method = RequestMethod.GET) 25 | public List getUserByNickname(String nickname) { 26 | return userService.getUserByNickname(nickname); 27 | } 28 | 29 | @RequestMapping(value = "/user/{id}", method = RequestMethod.GET) 30 | public User getUserById(@PathVariable Long id) { 31 | return userService.getUserById(id); 32 | } 33 | 34 | @RequestMapping(value = "/roles", method = RequestMethod.GET) 35 | public List getAllRole() { 36 | return userService.getAllRole(); 37 | } 38 | 39 | @RequestMapping(value = "/user/enabled", method = RequestMethod.PUT) 40 | public RespBean updateUserEnabled(Boolean enabled, Long uid) { 41 | if (userService.updateUserEnabled(enabled, uid) == 1) { 42 | return new RespBean("success", "更新成功!"); 43 | } else { 44 | return new RespBean("error", "更新失败!"); 45 | } 46 | } 47 | 48 | @RequestMapping(value = "/user/{uid}", method = RequestMethod.DELETE) 49 | public RespBean deleteUserById(@PathVariable Long uid) { 50 | if (userService.deleteUserById(uid) == 1) { 51 | return new RespBean("success", "删除成功!"); 52 | } else { 53 | return new RespBean("error", "删除失败!"); 54 | } 55 | } 56 | 57 | @RequestMapping(value = "/user/role", method = RequestMethod.PUT) 58 | public RespBean updateUserRoles(Long[] rids, Long id) { 59 | if (userService.updateUserRoles(rids, id) == rids.length) { 60 | return new RespBean("success", "更新成功!"); 61 | } else { 62 | return new RespBean("error", "更新失败!"); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/mapper/ArticleMapper.java: -------------------------------------------------------------------------------- 1 | package org.sang.mapper; 2 | 3 | import org.apache.ibatis.annotations.Mapper; 4 | import org.apache.ibatis.annotations.Param; 5 | import org.sang.bean.Article; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by sang on 2017/12/20. 11 | */ 12 | @Mapper 13 | public interface ArticleMapper { 14 | int addNewArticle(Article article); 15 | 16 | int updateArticle(Article article); 17 | 18 | List
getArticleByState(@Param("state") Integer state, @Param("start") Integer start, @Param("count") Integer count, @Param("uid") Long uid,@Param("keywords") String keywords); 19 | 20 | // List
getArticleByStateByAdmin(@Param("start") int start, @Param("count") Integer count, @Param("keywords") String keywords); 21 | 22 | int getArticleCountByState(@Param("state") Integer state, @Param("uid") Long uid, @Param("keywords") String keywords); 23 | 24 | int updateArticleState(@Param("aids") Long aids[], @Param("state") Integer state); 25 | 26 | int updateArticleStateById(@Param("articleId") Integer articleId, @Param("state") Integer state); 27 | 28 | int deleteArticleById(@Param("aids") Long[] aids); 29 | 30 | Article getArticleById(Long aid); 31 | 32 | void pvIncrement(Long aid); 33 | 34 | //INSERT INTO pv(countDate,pv,uid) SELECT NOW(),SUM(pageView),uid FROM article GROUP BY uid 35 | void pvStatisticsPerDay(); 36 | 37 | List getCategories(Long uid); 38 | 39 | List getDataStatistics(Long uid); 40 | } 41 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/mapper/ArticleMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | INSERT INTO article SET title=#{title},mdContent=#{mdContent},htmlContent=#{htmlContent},summary=#{summary},cid=#{cid},uid=#{uid},publishDate=#{publishDate},state=#{state},editTime=#{editTime} 8 | 9 | 10 | UPDATE article set pageView=pageView+1 WHERE id=#{aid} 11 | 12 | 13 | UPDATE article SET 14 | title=#{title},mdContent=#{mdContent},htmlContent=#{htmlContent},summary=#{summary},cid=#{cid},editTime=#{editTime} 15 | 16 | ,state=1 17 | 18 | 19 | ,publishDate=#{publishDate} 20 | 21 | WHERE id=#{id} 22 | 23 | 40 | 48 | 62 | 63 | UPDATE article SET state=#{state} WHERE id IN 64 | 65 | #{aid} 66 | 67 | 68 | 69 | UPDATE article SET state=#{state} WHERE id = #{articleId} 70 | 71 | 72 | DELETE FROM article WHERE id IN 73 | 74 | #{aid} 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | INSERT INTO pv(countDate,pv,uid) SELECT CURRENT_DATE(),totalPv-pv,t.`uid` FROM pvview p,totalpvview t WHERE p.`uid`=t.`uid` 103 | 104 | 105 | 108 | 111 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/mapper/CategoryMapper.java: -------------------------------------------------------------------------------- 1 | package org.sang.mapper; 2 | 3 | import org.apache.ibatis.annotations.Mapper; 4 | import org.apache.ibatis.annotations.Param; 5 | import org.sang.bean.Category; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by sang on 2017/12/19. 11 | */ 12 | @Mapper 13 | public interface CategoryMapper { 14 | List getAllCategories(); 15 | 16 | int deleteCategoryByIds(@Param("ids") String[] ids); 17 | 18 | int updateCategoryById(Category category); 19 | 20 | int addCategory(Category category); 21 | } 22 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/mapper/CategoryMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | DELETE FROM category WHERE id IN 11 | 12 | #{id} 13 | 14 | 15 | 16 | UPDATE category SET cateName=#{cateName} WHERE id=#{id} 17 | 18 | 19 | INSERT INTO category SET date=#{date},cateName=#{cateName} 20 | 21 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/mapper/RolesMapper.java: -------------------------------------------------------------------------------- 1 | package org.sang.mapper; 2 | 3 | import org.apache.ibatis.annotations.Mapper; 4 | import org.apache.ibatis.annotations.Param; 5 | import org.sang.bean.Role; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by sang on 2017/12/17. 11 | */ 12 | @Mapper 13 | public interface RolesMapper { 14 | int addRoles(@Param("roles") String[] roles, @Param("uid") Long uid); 15 | 16 | List getRolesByUid(Long uid); 17 | } 18 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/mapper/RolesMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | INSERT INTO roles_user VALUES 8 | 9 | (null,#{role},#{uid}) 10 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/mapper/TagsMapper.java: -------------------------------------------------------------------------------- 1 | package org.sang.mapper; 2 | 3 | import org.apache.ibatis.annotations.Mapper; 4 | import org.apache.ibatis.annotations.Param; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by sang on 2017/12/21. 10 | */ 11 | @Mapper 12 | public interface TagsMapper { 13 | int deleteTagsByAid(Long aid); 14 | 15 | int saveTags(@Param("tags") String[] tags); 16 | 17 | List getTagsIdByTagName(@Param("tagNames") String[] tagNames); 18 | 19 | int saveTags2ArticleTags(@Param("tagIds") List tagIds, @Param("aid") Long aid); 20 | } 21 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/mapper/TagsMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | DELETE FROM article_tags WHERE aid=#{aid} 8 | 9 | 10 | INSERT IGNORE INTO tags(tagName) VALUES 11 | 12 | (#{tag}) 13 | 14 | 15 | 21 | 22 | INSERT INTO article_tags(aid,tid) VALUES 23 | 24 | (#{aid},#{tagId}) 25 | 26 | 27 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package org.sang.mapper; 2 | 3 | import org.apache.ibatis.annotations.Mapper; 4 | import org.apache.ibatis.annotations.Param; 5 | import org.sang.bean.Role; 6 | import org.sang.bean.User; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by sang on 2017/12/17. 12 | */ 13 | @Mapper 14 | public interface UserMapper { 15 | 16 | User loadUserByUsername(@Param("username") String username); 17 | 18 | long reg(User user); 19 | 20 | int updateUserEmail(@Param("email") String email, @Param("id") Long id); 21 | 22 | List getUserByNickname(@Param("nickname") String nickname); 23 | 24 | List getAllRole(); 25 | 26 | int updateUserEnabled(@Param("enabled") Boolean enabled, @Param("uid") Long uid); 27 | 28 | int deleteUserById(Long uid); 29 | 30 | int deleteUserRolesByUid(Long id); 31 | 32 | int setUserRoles(@Param("rids") Long[] rids, @Param("id") Long id); 33 | 34 | User getUserById(@Param("id") Long id); 35 | } 36 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/mapper/UserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | INSERT INTO user set username=#{username},password=#{password},nickname=#{nickname} 11 | 12 | 13 | UPDATE user set email=#{email} WHERE id=#{id} 14 | 15 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | UPDATE user set enabled=#{enabled} WHERE id=#{uid} 50 | 51 | 52 | DELETE FROM user WHERE id=#{id} 53 | 54 | 55 | DELETE FROM roles_user WHERE uid=#{id} 56 | 57 | 58 | INSERT INTO roles_user(rid,uid) VALUES 59 | 60 | (#{rid},#{id}) 61 | 62 | 63 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/service/ArticleService.java: -------------------------------------------------------------------------------- 1 | package org.sang.service; 2 | 3 | import org.sang.bean.Article; 4 | import org.sang.mapper.ArticleMapper; 5 | import org.sang.mapper.TagsMapper; 6 | import org.sang.utils.Util; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | 11 | import java.sql.Timestamp; 12 | import java.util.Date; 13 | import java.util.List; 14 | 15 | /** 16 | * Created by sang on 2017/12/20. 17 | */ 18 | @Service 19 | @Transactional 20 | public class ArticleService { 21 | @Autowired 22 | ArticleMapper articleMapper; 23 | @Autowired 24 | TagsMapper tagsMapper; 25 | 26 | public int addNewArticle(Article article) { 27 | //处理文章摘要 28 | if (article.getSummary() == null || "".equals(article.getSummary())) { 29 | //直接截取 30 | String stripHtml = stripHtml(article.getHtmlContent()); 31 | article.setSummary(stripHtml.substring(0, stripHtml.length() > 50 ? 50 : stripHtml.length())); 32 | } 33 | if (article.getId() == -1) { 34 | //添加操作 35 | Timestamp timestamp = new Timestamp(System.currentTimeMillis()); 36 | if (article.getState() == 1) { 37 | //设置发表日期 38 | article.setPublishDate(timestamp); 39 | } 40 | article.setEditTime(timestamp); 41 | //设置当前用户 42 | article.setUid(Util.getCurrentUser().getId()); 43 | int i = articleMapper.addNewArticle(article); 44 | //打标签 45 | String[] dynamicTags = article.getDynamicTags(); 46 | if (dynamicTags != null && dynamicTags.length > 0) { 47 | int tags = addTagsToArticle(dynamicTags, article.getId()); 48 | if (tags == -1) { 49 | return tags; 50 | } 51 | } 52 | return i; 53 | } else { 54 | Timestamp timestamp = new Timestamp(System.currentTimeMillis()); 55 | if (article.getState() == 1) { 56 | //设置发表日期 57 | article.setPublishDate(timestamp); 58 | } 59 | //更新 60 | article.setEditTime(new Timestamp(System.currentTimeMillis())); 61 | int i = articleMapper.updateArticle(article); 62 | //修改标签 63 | String[] dynamicTags = article.getDynamicTags(); 64 | if (dynamicTags != null && dynamicTags.length > 0) { 65 | int tags = addTagsToArticle(dynamicTags, article.getId()); 66 | if (tags == -1) { 67 | return tags; 68 | } 69 | } 70 | return i; 71 | } 72 | } 73 | 74 | private int addTagsToArticle(String[] dynamicTags, Long aid) { 75 | //1.删除该文章目前所有的标签 76 | tagsMapper.deleteTagsByAid(aid); 77 | //2.将上传上来的标签全部存入数据库 78 | tagsMapper.saveTags(dynamicTags); 79 | //3.查询这些标签的id 80 | List tIds = tagsMapper.getTagsIdByTagName(dynamicTags); 81 | //4.重新给文章设置标签 82 | int i = tagsMapper.saveTags2ArticleTags(tIds, aid); 83 | return i == dynamicTags.length ? i : -1; 84 | } 85 | 86 | public String stripHtml(String content) { 87 | content = content.replaceAll("

", ""); 88 | content = content.replaceAll("", ""); 89 | content = content.replaceAll("\\<.*?>", ""); 90 | return content; 91 | } 92 | 93 | public List

getArticleByState(Integer state, Integer page, Integer count,String keywords) { 94 | int start = (page - 1) * count; 95 | Long uid = Util.getCurrentUser().getId(); 96 | return articleMapper.getArticleByState(state, start, count, uid,keywords); 97 | } 98 | 99 | // public List
getArticleByStateByAdmin(Integer page, Integer count,String keywords) { 100 | // int start = (page - 1) * count; 101 | // return articleMapper.getArticleByStateByAdmin(start, count,keywords); 102 | // } 103 | 104 | public int getArticleCountByState(Integer state, Long uid,String keywords) { 105 | return articleMapper.getArticleCountByState(state, uid,keywords); 106 | } 107 | 108 | public int updateArticleState(Long[] aids, Integer state) { 109 | if (state == 2) { 110 | return articleMapper.deleteArticleById(aids); 111 | } else { 112 | return articleMapper.updateArticleState(aids, 2);//放入到回收站中 113 | } 114 | } 115 | 116 | public int restoreArticle(Integer articleId) { 117 | return articleMapper.updateArticleStateById(articleId, 1); // 从回收站还原在原处 118 | } 119 | 120 | public Article getArticleById(Long aid) { 121 | Article article = articleMapper.getArticleById(aid); 122 | articleMapper.pvIncrement(aid); 123 | return article; 124 | } 125 | 126 | public void pvStatisticsPerDay() { 127 | articleMapper.pvStatisticsPerDay(); 128 | } 129 | 130 | /** 131 | * 获取最近七天的日期 132 | * @return 133 | */ 134 | public List getCategories() { 135 | return articleMapper.getCategories(Util.getCurrentUser().getId()); 136 | } 137 | 138 | /** 139 | * 获取最近七天的数据 140 | * @return 141 | */ 142 | public List getDataStatistics() { 143 | return articleMapper.getDataStatistics(Util.getCurrentUser().getId()); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/service/CategoryService.java: -------------------------------------------------------------------------------- 1 | package org.sang.service; 2 | 3 | import org.sang.bean.Category; 4 | import org.sang.mapper.CategoryMapper; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | import java.sql.Timestamp; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by sang on 2017/12/19. 14 | */ 15 | @Service 16 | @Transactional 17 | public class CategoryService { 18 | @Autowired 19 | CategoryMapper categoryMapper; 20 | 21 | public List getAllCategories() { 22 | return categoryMapper.getAllCategories(); 23 | } 24 | 25 | public boolean deleteCategoryByIds(String ids) { 26 | String[] split = ids.split(","); 27 | int result = categoryMapper.deleteCategoryByIds(split); 28 | return result == split.length; 29 | } 30 | 31 | public int updateCategoryById(Category category) { 32 | return categoryMapper.updateCategoryById(category); 33 | } 34 | 35 | public int addCategory(Category category) { 36 | category.setDate(new Timestamp(System.currentTimeMillis())); 37 | return categoryMapper.addCategory(category); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/service/DataStatisticsComponent.java: -------------------------------------------------------------------------------- 1 | package org.sang.service; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.scheduling.annotation.Scheduled; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * Created by sang on 2017/12/25. 9 | */ 10 | @Component 11 | public class DataStatisticsComponent { 12 | @Autowired 13 | ArticleService articleService; 14 | 15 | //每天执行一次,统计PV 16 | @Scheduled(cron = "1 0 0 * * ?") 17 | public void pvStatisticsPerDay() { 18 | articleService.pvStatisticsPerDay(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/service/UserService.java: -------------------------------------------------------------------------------- 1 | package org.sang.service; 2 | 3 | import org.sang.bean.Role; 4 | import org.sang.bean.User; 5 | import org.sang.mapper.RolesMapper; 6 | import org.sang.mapper.UserMapper; 7 | import org.sang.utils.Util; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.security.core.userdetails.UserDetailsService; 11 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 12 | import org.springframework.stereotype.Service; 13 | import org.springframework.transaction.annotation.Transactional; 14 | import org.springframework.util.DigestUtils; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * Created by sang on 2017/12/17. 20 | */ 21 | @Service 22 | @Transactional 23 | public class UserService implements UserDetailsService { 24 | @Autowired 25 | UserMapper userMapper; 26 | @Autowired 27 | RolesMapper rolesMapper; 28 | 29 | @Override 30 | public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { 31 | User user = userMapper.loadUserByUsername(s); 32 | if (user == null) { 33 | //避免返回null,这里返回一个不含有任何值的User对象,在后期的密码比对过程中一样会验证失败 34 | return new User(); 35 | } 36 | //查询用户的角色信息,并返回存入user中 37 | List roles = rolesMapper.getRolesByUid(user.getId()); 38 | user.setRoles(roles); 39 | return user; 40 | } 41 | 42 | /** 43 | * @param user 44 | * @return 0表示成功 45 | * 1表示用户名重复 46 | * 2表示失败 47 | */ 48 | public int reg(User user) { 49 | User loadUserByUsername = userMapper.loadUserByUsername(user.getUsername()); 50 | if (loadUserByUsername != null) { 51 | return 1; 52 | } 53 | //插入用户,插入之前先对密码进行加密 54 | user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes())); 55 | user.setEnabled(true);//用户可用 56 | long result = userMapper.reg(user); 57 | //配置用户的角色,默认都是普通用户 58 | String[] roles = new String[]{"2"}; 59 | int i = rolesMapper.addRoles(roles, user.getId()); 60 | boolean b = i == roles.length && result == 1; 61 | if (b) { 62 | return 0; 63 | } else { 64 | return 2; 65 | } 66 | } 67 | 68 | public int updateUserEmail(String email) { 69 | return userMapper.updateUserEmail(email, Util.getCurrentUser().getId()); 70 | } 71 | 72 | public List getUserByNickname(String nickname) { 73 | List list = userMapper.getUserByNickname(nickname); 74 | return list; 75 | } 76 | 77 | public List getAllRole() { 78 | return userMapper.getAllRole(); 79 | } 80 | 81 | public int updateUserEnabled(Boolean enabled, Long uid) { 82 | return userMapper.updateUserEnabled(enabled, uid); 83 | } 84 | 85 | public int deleteUserById(Long uid) { 86 | return userMapper.deleteUserById(uid); 87 | } 88 | 89 | public int updateUserRoles(Long[] rids, Long id) { 90 | int i = userMapper.deleteUserRolesByUid(id); 91 | return userMapper.setUserRoles(rids, id); 92 | } 93 | 94 | public User getUserById(Long id) { 95 | return userMapper.getUserById(id); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/utils/DateTypeHandler.java: -------------------------------------------------------------------------------- 1 | package org.sang.utils; 2 | 3 | import org.apache.ibatis.type.JdbcType; 4 | import org.apache.ibatis.type.MappedJdbcTypes; 5 | import org.apache.ibatis.type.MappedTypes; 6 | import org.apache.ibatis.type.TypeHandler; 7 | 8 | import java.sql.CallableStatement; 9 | import java.sql.PreparedStatement; 10 | import java.sql.ResultSet; 11 | import java.sql.SQLException; 12 | import java.text.SimpleDateFormat; 13 | 14 | /** 15 | * Created by sang on 2017/12/25. 16 | */ 17 | @MappedJdbcTypes(JdbcType.DATE) 18 | @MappedTypes(String.class) 19 | public class DateTypeHandler implements TypeHandler { 20 | private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 21 | 22 | @Override 23 | public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { 24 | 25 | } 26 | 27 | @Override 28 | public String getResult(ResultSet rs, String columnName) throws SQLException { 29 | return sdf.format(rs.getDate(columnName)); 30 | } 31 | 32 | @Override 33 | public String getResult(ResultSet rs, int columnIndex) throws SQLException { 34 | return sdf.format(rs.getDate(columnIndex)); 35 | } 36 | 37 | @Override 38 | public String getResult(CallableStatement cs, int columnIndex) throws SQLException { 39 | return sdf.format(cs.getDate(columnIndex)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /blogserver/src/main/java/org/sang/utils/Util.java: -------------------------------------------------------------------------------- 1 | package org.sang.utils; 2 | 3 | import org.sang.bean.User; 4 | import org.springframework.security.core.context.SecurityContextHolder; 5 | 6 | /** 7 | * Created by sang on 2017/12/20. 8 | */ 9 | public class Util { 10 | public static User getCurrentUser() { 11 | User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 12 | return user; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /blogserver/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/blogserver/src/main/resources/application.properties -------------------------------------------------------------------------------- /blogserver/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=DEBUG,stdout 2 | log4j.logger.org.mybatis=DEBUG 3 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 4 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n -------------------------------------------------------------------------------- /blogserver/src/main/resources/mybatis-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /blogserver/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | V部落
-------------------------------------------------------------------------------- /blogserver/src/main/resources/static/static/fonts/element-icons.6f0a763.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/blogserver/src/main/resources/static/static/fonts/element-icons.6f0a763.ttf -------------------------------------------------------------------------------- /blogserver/src/main/resources/static/static/fonts/fontawesome-webfont.674f50d.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/blogserver/src/main/resources/static/static/fonts/fontawesome-webfont.674f50d.eot -------------------------------------------------------------------------------- /blogserver/src/main/resources/static/static/fonts/fontawesome-webfont.af7ae50.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/blogserver/src/main/resources/static/static/fonts/fontawesome-webfont.af7ae50.woff2 -------------------------------------------------------------------------------- /blogserver/src/main/resources/static/static/fonts/fontawesome-webfont.b06871f.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/blogserver/src/main/resources/static/static/fonts/fontawesome-webfont.b06871f.ttf -------------------------------------------------------------------------------- /blogserver/src/main/resources/static/static/fonts/fontawesome-webfont.fee66e7.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/blogserver/src/main/resources/static/static/fonts/fontawesome-webfont.fee66e7.woff -------------------------------------------------------------------------------- /blogserver/src/main/resources/static/static/fonts/fontello.068ca2b.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/blogserver/src/main/resources/static/static/fonts/fontello.068ca2b.ttf -------------------------------------------------------------------------------- /blogserver/src/main/resources/static/static/fonts/fontello.e73a064.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/blogserver/src/main/resources/static/static/fonts/fontello.e73a064.eot -------------------------------------------------------------------------------- /blogserver/src/main/resources/static/static/img/fontello.9354499.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (C) 2017 by original authors @ fontello.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /blogserver/src/main/resources/static/static/js/app.ca4cf1cc4fa16a3105b3.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([1],{"0b4w":function(e,t){},"7XKW":function(e,t){},LOOz:function(e,t){},NHnr:function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=a("7+uW"),i={render:function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticStyle:{"background-color":"rgba(235, 235, 235, 0.08)"},attrs:{id:"app"}},[t("router-view")],1)},staticRenderFns:[]},s=a("VU/8")({name:"app"},i,!1,function(e){a("d4F1")},null,null).exports,o=a("/ocq"),l=a("mtWM"),r=a.n(l),c=function(e,t){return r()({method:"post",url:""+e,data:t,transformRequest:[function(e){var t="";for(var a in e)t+=encodeURIComponent(a)+"="+encodeURIComponent(e[a])+"&";return t}],headers:{"Content-Type":"application/x-www-form-urlencoded"}})},d=function(e,t){return r()({method:"put",url:""+e,data:t,transformRequest:[function(e){var t="";for(var a in e)t+=encodeURIComponent(a)+"="+encodeURIComponent(e[a])+"&";return t}],headers:{"Content-Type":"application/x-www-form-urlencoded"}})},u=function(e){return r()({method:"delete",url:""+e})},m=function(e,t){return r()({method:"get",data:t,transformRequest:[function(e){var t="";for(var a in e)t+=encodeURIComponent(a)+"="+encodeURIComponent(e[a])+"&";return t}],headers:{"Content-Type":"application/x-www-form-urlencoded"},url:""+e})},h={data:function(){return{rules:{account:[{required:!0,message:"请输入用户名",trigger:"blur"}],checkPass:[{required:!0,message:"请输入密码",trigger:"blur"}]},checked:!0,loginForm:{username:"sang",password:"123"},loading:!1}},methods:{submitClick:function(){var e=this;this.loading=!0,c("/login",{username:this.loginForm.username,password:this.loginForm.password}).then(function(t){if(e.loading=!1,200==t.status){"success"==t.data.status?e.$router.replace({path:"/home"}):e.$alert("登录失败!","失败!")}else e.$alert("登录失败!","失败!")},function(t){e.loading=!1,e.$alert("找不到服务器⊙﹏⊙∥!","失败!")})}}},p={render:function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("el-form",{directives:[{name:"loading",rawName:"v-loading",value:e.loading,expression:"loading"}],staticClass:"login-container",attrs:{rules:e.rules,"label-position":"left","label-width":"0px"}},[a("h3",{staticClass:"login_title"},[e._v("系统登录")]),e._v(" "),a("el-form-item",{attrs:{prop:"account"}},[a("el-input",{attrs:{type:"text","auto-complete":"off",placeholder:"账号"},model:{value:e.loginForm.username,callback:function(t){e.$set(e.loginForm,"username",t)},expression:"loginForm.username"}})],1),e._v(" "),a("el-form-item",{attrs:{prop:"checkPass"}},[a("el-input",{attrs:{type:"password","auto-complete":"off",placeholder:"密码"},model:{value:e.loginForm.password,callback:function(t){e.$set(e.loginForm,"password",t)},expression:"loginForm.password"}})],1),e._v(" "),a("el-checkbox",{staticClass:"login_remember",attrs:{"label-position":"left"},model:{value:e.checked,callback:function(t){e.checked=t},expression:"checked"}},[e._v("记住密码")]),e._v(" "),a("el-form-item",{staticStyle:{width:"100%"}},[a("el-button",{staticStyle:{width:"100%"},attrs:{type:"primary"},nativeOn:{click:function(t){t.preventDefault(),e.submitClick(t)}}},[e._v("登录")])],1)],1)},staticRenderFns:[]},g=a("VU/8")(h,p,!1,function(e){a("bKeH")},null,null).exports,f={methods:{handleCommand:function(e){var t=this;"logout"==e&&this.$confirm("注销登录吗?","提示",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){m("/logout"),t.currentUserName="游客",t.$router.replace({path:"/"})},function(){})}},mounted:function(){this.$alert("为了确保所有的小伙伴都能看到完整的数据演示,数据库只开放了查询权限和部分字段的更新权限,其他权限都不具备,完整权限的演示需要大家在自己本地部署后,换一个正常的数据库用户后即可查看,这点请大家悉知!","友情提示",{confirmButtonText:"确定",callback:function(e){}});var e=this;m("/currentUserName").then(function(t){e.currentUserName=t.data},function(t){e.currentUserName="游客"})},data:function(){return{currentUserName:""}}},v={render:function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("el-container",{staticClass:"home_container"},[a("el-header",[a("div",{staticClass:"home_title"},[e._v("V部落博客管理平台")]),e._v(" "),a("div",{staticClass:"home_userinfoContainer"},[a("el-dropdown",{on:{command:e.handleCommand}},[a("span",{staticClass:"el-dropdown-link home_userinfo"},[e._v("\n "+e._s(e.currentUserName)),a("i",{staticClass:"el-icon-arrow-down el-icon--right home_userinfo"})]),e._v(" "),a("el-dropdown-menu",{attrs:{slot:"dropdown"},slot:"dropdown"},[a("el-dropdown-item",{attrs:{command:"sysMsg"}},[e._v("系统消息")]),e._v(" "),a("el-dropdown-item",{attrs:{command:"MyArticle"}},[e._v("我的文章")]),e._v(" "),a("el-dropdown-item",{attrs:{command:"MyHome"}},[e._v("个人主页")]),e._v(" "),a("el-dropdown-item",{attrs:{command:"logout",divided:""}},[e._v("退出登录")])],1)],1)],1)]),e._v(" "),a("el-container",[a("el-aside",{attrs:{width:"200px"}},[a("el-menu",{staticClass:"el-menu-vertical-demo",staticStyle:{"background-color":"#ECECEC"},attrs:{"default-active":"0",router:""}},[e._l(this.$router.options.routes,function(t,n){return t.hidden?e._e():[t.children.length>1?a("el-submenu",{key:n,attrs:{index:n+""}},[a("template",{slot:"title"},[a("i",{class:t.iconCls}),e._v(" "),a("span",[e._v(e._s(t.name))])]),e._v(" "),e._l(t.children,function(t){return t.hidden?e._e():a("el-menu-item",{key:t.path,attrs:{index:t.path}},[e._v("\n "+e._s(t.name)+"\n ")])})],2):[a("el-menu-item",{attrs:{index:t.children[0].path}},[a("i",{class:t.children[0].iconCls}),e._v(" "),a("span",{attrs:{slot:"title"},slot:"title"},[e._v(e._s(t.children[0].name))])])]]})],2)],1),e._v(" "),a("el-container",[a("el-main",[a("el-breadcrumb",{attrs:{"separator-class":"el-icon-arrow-right"}},[a("el-breadcrumb-item",{attrs:{to:{path:"/home"}}},[e._v("首页")]),e._v(" "),a("el-breadcrumb-item",{domProps:{textContent:e._s(this.$router.currentRoute.name)}})],1),e._v(" "),a("keep-alive",[this.$route.meta.keepAlive?a("router-view"):e._e()],1),e._v(" "),this.$route.meta.keepAlive?e._e():a("router-view")],1)],1)],1)],1)},staticRenderFns:[]},y=a("VU/8")(f,v,!1,function(e){a("0b4w")},null,null).exports,_={data:function(){return{articles:[],selItems:[],loading:!1,currentPage:1,totalCount:-1,pageSize:6,keywords:"",dustbinData:[]}},mounted:function(){var e=this;this.loading=!0,this.loadBlogs(1,this.pageSize);e=this;window.bus.$on("blogTableReload",function(){e.loading=!0,e.loadBlogs(e.currentPage,e.pageSize)})},methods:{searchClick:function(){this.loadBlogs(1,this.pageSize)},itemClick:function(e){this.$router.push({path:"/blogDetail",query:{aid:e.id}})},deleteMany:function(){for(var e=this.selItems,t=0;t0&&e.showDelete,expression:"this.articles.length>0 && showDelete"}],staticStyle:{margin:"0px"},attrs:{type:"danger",size:"mini",disabled:0==this.selItems.length},on:{click:e.deleteMany}},[e._v("批量删除\n ")]),e._v(" "),a("span"),e._v(" "),a("el-pagination",{directives:[{name:"show",rawName:"v-show",value:this.articles.length>0,expression:"this.articles.length>0"}],attrs:{background:"","page-size":e.pageSize,layout:"prev, pager, next",total:e.totalCount},on:{"current-change":e.currentChange}})],1)],1)},staticRenderFns:[]},b={data:function(){return{emailValidateForm:{email:""},loading:!1}},mounted:function(){var e=this;m("/currentUserEmail").then(function(t){200==t.status&&(e.emailValidateForm.email=t.data)})},methods:{submitForm:function(e){var t=this;this.$refs[e].validate(function(e){if(!e)return t.$message({type:"error",message:"邮箱格式不对哦!"}),!1;t.loading=!0,d("/updateUserEmail",{email:t.emailValidateForm.email}).then(function(e){t.loading=!1,200==e.status?t.$message({type:e.data.status,message:e.data.msg}):t.$message({type:"error",message:"开启失败!"})},function(e){t.loading=!1,t.$message({type:"error",message:"开启失败!"})})})}}},x={render:function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("el-card",{directives:[{name:"loading",rawName:"v-loading",value:e.loading,expression:"loading"}],staticStyle:{width:"500px"}},[a("div",[a("div",{staticStyle:{"text-align":"left"}},[a("el-form",{ref:"emailValidateForm",staticStyle:{color:"#20a0ff","font-size":"14px"},attrs:{model:e.emailValidateForm,"label-position":"top"}},[a("el-form-item",{attrs:{prop:"email",label:"开启博客评论通知",rules:[{type:"email",message:"邮箱格式不对哦!"}]}},[a("el-input",{staticStyle:{width:"300px"},attrs:{type:"email","auto-complete":"off",placeholder:"请输入邮箱地址...",size:"mini"},model:{value:e.emailValidateForm.email,callback:function(t){e.$set(e.emailValidateForm,"email",t)},expression:"emailValidateForm.email"}}),e._v(" "),a("el-button",{attrs:{type:"primary",size:"mini"},on:{click:function(t){e.submitForm("emailValidateForm")}}},[e._v("确定")])],1)],1)],1)])])},staticRenderFns:[]},$={mounted:function(){var e=this;m("/isAdmin").then(function(t){200==t.status&&(e.isAdmin=t.data)})},data:function(){return{activeName:"post",isAdmin:!1}},methods:{handleClick:function(e,t){}},components:{blog_table:a("VU/8")(_,w,!1,function(e){a("sdRM")},null,null).exports,blog_cfg:a("VU/8")(b,x,!1,null,null,null).exports}},k={render:function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("el-container",{staticClass:"article_list"},[a("el-main",{staticClass:"main"},[a("el-tabs",{attrs:{type:"card"},on:{"tab-click":e.handleClick},model:{value:e.activeName,callback:function(t){e.activeName=t},expression:"activeName"}},[a("el-tab-pane",{attrs:{label:"全部文章",name:"all"}},[a("blog_table",{attrs:{state:"-1",showEdit:!1,showDelete:!1,showRestore:!1,activeName:e.activeName}})],1),e._v(" "),a("el-tab-pane",{attrs:{label:"已发表",name:"post"}},[a("blog_table",{attrs:{state:"1",showEdit:!0,showDelete:!0,showRestore:!1,activeName:e.activeName}})],1),e._v(" "),a("el-tab-pane",{attrs:{label:"草稿箱",name:"draft"}},[a("blog_table",{attrs:{state:"0",showEdit:!0,showDelete:!0,showRestore:!1,activeName:e.activeName}})],1),e._v(" "),a("el-tab-pane",{attrs:{label:"回收站",name:"dustbin"}},[a("blog_table",{attrs:{state:"2",showEdit:!1,showDelete:!0,showRestore:!0,activeName:e.activeName}})],1),e._v(" "),e.isAdmin?a("el-tab-pane",{attrs:{label:"博客管理",name:"blogmana"}},[a("blog_table",{attrs:{state:"-2",showEdit:!1,showDelete:!0,showRestore:!1,activeName:e.activeName}})],1):e._e(),e._v(" "),a("el-tab-pane",{attrs:{label:"博客配置",name:"blogcfg"}},[a("blog_cfg")],1)],1)],1)],1)},staticRenderFns:[]},C=a("VU/8")($,k,!1,function(e){a("an7X")},null,null).exports,S={methods:{addNewCate:function(){this.loading=!0;var e=this;c("/admin/category/",{cateName:this.cateName}).then(function(t){if(200==t.status){var a=t.data;e.$message({type:a.status,message:a.msg}),e.cateName="",e.refresh()}e.loading=!1},function(t){403==t.response.status&&e.$message({type:"error",message:t.response.data}),e.loading=!1})},deleteAll:function(){var e=this;this.$confirm("确认删除这 "+this.selItems.length+" 条数据?","提示",{type:"warning",confirmButtonText:"确定",cancelButtonText:"取消"}).then(function(){for(var t=e.selItems,a="",n=0;n0?a("el-button",{staticStyle:{"margin-top":"10px",width:"100px"},attrs:{type:"danger",disabled:0==this.selItems.length},on:{click:e.deleteAll}},[e._v("批量删除\n ")]):e._e()],1)],1)},staticRenderFns:[]},T=a("VU/8")(S,R,!1,function(e){a("kuxu")},null,null).exports,z=a("Gu7T"),N=a.n(z),D=a("Icdr"),U=a.n(D),E=a("y1vT"),F=a.n(E),B=["legendselectchanged","legendselected","legendunselected","datazoom","datarangeselected","timelinechanged","timelineplaychanged","restore","dataviewchanged","magictypechanged","geoselectchanged","geoselected","geounselected","pieselectchanged","pieselected","pieunselected","mapselectchanged","mapselected","mapunselected","axisareaselected","focusnodeadjacency","unfocusnodeadjacency","brush","brushselected"],V=["click","dblclick","mouseover","mouseout","mousedown","mouseup","globalout"],I={props:{options:Object,theme:[String,Object],initOptions:Object,group:String,autoResize:Boolean,watchShallow:Boolean},data:function(){return{chart:null}},computed:{width:{cache:!1,get:function(){return this.delegateGet("width","getWidth")}},height:{cache:!1,get:function(){return this.delegateGet("height","getHeight")}},isDisposed:{cache:!1,get:function(){return!!this.delegateGet("isDisposed","isDisposed")}},computedOptions:{cache:!1,get:function(){return this.delegateGet("computedOptions","getOption")}}},watch:{group:function(e){this.chart.group=e}},methods:{mergeOptions:function(e,t,a){this.delegateMethod("setOption",e,t,a)},resize:function(e){this.delegateMethod("resize",e)},dispatchAction:function(e){this.delegateMethod("dispatchAction",e)},convertToPixel:function(e,t){return this.delegateMethod("convertToPixel",e,t)},convertFromPixel:function(e,t){return this.delegateMethod("convertFromPixel",e,t)},containPixel:function(e,t){return this.delegateMethod("containPixel",e,t)},showLoading:function(e,t){this.delegateMethod("showLoading",e,t)},hideLoading:function(){this.delegateMethod("hideLoading")},getDataURL:function(e){return this.delegateMethod("getDataURL",e)},getConnectedDataURL:function(e){return this.delegateMethod("getConnectedDataURL",e)},clear:function(){this.delegateMethod("clear")},dispose:function(){this.delegateMethod("dispose")},delegateMethod:function(e){var t;if(this.chart){for(var a=arguments.length,i=Array(a>1?a-1:0),s=1;s",components:{App:s}})},an7X:function(e,t){},bKeH:function(e,t){},d4F1:function(e,t){},e0XP:function(e,t){},gYbd:function(e,t){},kuxu:function(e,t){},pw1w:function(e,t){},sdRM:function(e,t){},tvR6:function(e,t){}},["NHnr"]); 2 | //# sourceMappingURL=app.ca4cf1cc4fa16a3105b3.js.map -------------------------------------------------------------------------------- /blogserver/src/main/resources/static/static/js/manifest.35480ac00baae1b754e6.js: -------------------------------------------------------------------------------- 1 | !function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,a){for(var i,u,f,s=0,l=[];s 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

01.html

9 | 10 | -------------------------------------------------------------------------------- /docs/article.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/docs/article.png -------------------------------------------------------------------------------- /docs/category.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/docs/category.png -------------------------------------------------------------------------------- /docs/datastatistics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/docs/datastatistics.png -------------------------------------------------------------------------------- /docs/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/docs/login.png -------------------------------------------------------------------------------- /docs/post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/docs/post.png -------------------------------------------------------------------------------- /docs/qrcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/docs/qrcode.jpg -------------------------------------------------------------------------------- /docs/usermana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/docs/usermana.png -------------------------------------------------------------------------------- /docs/vblog-database-er.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/docs/vblog-database-er.png -------------------------------------------------------------------------------- /vueblog/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"] 12 | } 13 | -------------------------------------------------------------------------------- /vueblog/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /vueblog/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /vueblog/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | "postcss-import": {}, 7 | "autoprefixer": {} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vueblog/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // if you are using ts-loader, setting this to true will make tyescript errors show up during build 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /vueblog/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vueblog/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/vueblog/build/logo.png -------------------------------------------------------------------------------- /vueblog/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function (options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders (loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap 41 | }) 42 | }) 43 | } 44 | 45 | // Extract CSS when that option is specified 46 | // (which is the case during production build) 47 | if (options.extract) { 48 | return ExtractTextPlugin.extract({ 49 | use: loaders, 50 | fallback: 'vue-style-loader' 51 | }) 52 | } else { 53 | return ['vue-style-loader'].concat(loaders) 54 | } 55 | } 56 | 57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 58 | return { 59 | css: generateLoaders(), 60 | postcss: generateLoaders(), 61 | less: generateLoaders('less'), 62 | sass: generateLoaders('sass', { indentedSyntax: true }), 63 | scss: generateLoaders('sass'), 64 | stylus: generateLoaders('stylus'), 65 | styl: generateLoaders('stylus') 66 | } 67 | } 68 | 69 | // Generate loaders for standalone style files (outside of .vue) 70 | exports.styleLoaders = function (options) { 71 | const output = [] 72 | const loaders = exports.cssLoaders(options) 73 | 74 | for (const extension in loaders) { 75 | const loader = loaders[extension] 76 | output.push({ 77 | test: new RegExp('\\.' + extension + '$'), 78 | use: loader 79 | }) 80 | } 81 | 82 | return output 83 | } 84 | 85 | exports.createNotifierCallback = () => { 86 | const notifier = require('node-notifier') 87 | 88 | return (severity, errors) => { 89 | if (severity !== 'error') return 90 | 91 | const error = errors[0] 92 | const filename = error.file && error.file.split('!').pop() 93 | 94 | notifier.notify({ 95 | title: packageConfig.name, 96 | message: severity + ': ' + error.name, 97 | subtitle: filename || '', 98 | icon: path.join(__dirname, 'logo.png') 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /vueblog/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vueblog/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | 12 | 13 | module.exports = { 14 | context: path.resolve(__dirname, '../'), 15 | entry: { 16 | app: './src/main.js' 17 | }, 18 | output: { 19 | path: config.build.assetsRoot, 20 | filename: '[name].js', 21 | publicPath: process.env.NODE_ENV === 'production' 22 | ? config.build.assetsPublicPath 23 | : config.dev.assetsPublicPath 24 | }, 25 | resolve: { 26 | extensions: ['.js', '.vue', '.json'], 27 | alias: { 28 | 'vue$': 'vue/dist/vue.esm.js', 29 | '@': resolve('src'), 30 | } 31 | }, 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.vue$/, 36 | loader: 'vue-loader', 37 | options: vueLoaderConfig 38 | }, 39 | { 40 | test: /\.js$/, 41 | loader: 'babel-loader', 42 | include: [resolve('src'), resolve('test')] 43 | }, 44 | { 45 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 46 | loader: 'url-loader', 47 | options: { 48 | limit: 10000, 49 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 50 | } 51 | }, 52 | { 53 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 54 | loader: 'url-loader', 55 | options: { 56 | limit: 10000, 57 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 58 | } 59 | }, 60 | { 61 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 62 | loader: 'url-loader', 63 | options: { 64 | limit: 10000, 65 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 66 | } 67 | } 68 | ] 69 | }, 70 | node: { 71 | // prevent webpack from injecting useless setImmediate polyfill because Vue 72 | // source contains it (although only uses it if it's native). 73 | setImmediate: false, 74 | // prevent webpack from injecting mocks to Node native modules 75 | // that does not make sense for the client 76 | dgram: 'empty', 77 | fs: 'empty', 78 | net: 'empty', 79 | tls: 'empty', 80 | child_process: 'empty' 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /vueblog/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const baseWebpackConfig = require('./webpack.base.conf') 7 | const HtmlWebpackPlugin = require('html-webpack-plugin') 8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 9 | const portfinder = require('portfinder') 10 | 11 | const HOST = process.env.HOST 12 | const PORT = process.env.PORT && Number(process.env.PORT) 13 | 14 | const devWebpackConfig = merge(baseWebpackConfig, { 15 | module: { 16 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 17 | }, 18 | // cheap-module-eval-source-map is faster for development 19 | devtool: config.dev.devtool, 20 | 21 | // these devServer options should be customized in /config/index.js 22 | devServer: { 23 | clientLogLevel: 'warning', 24 | historyApiFallback: true, 25 | hot: true, 26 | compress: true, 27 | host: HOST || config.dev.host, 28 | port: PORT || config.dev.port, 29 | open: config.dev.autoOpenBrowser, 30 | overlay: config.dev.errorOverlay 31 | ? { warnings: false, errors: true } 32 | : false, 33 | publicPath: config.dev.assetsPublicPath, 34 | proxy: config.dev.proxyTable, 35 | quiet: true, // necessary for FriendlyErrorsPlugin 36 | watchOptions: { 37 | poll: config.dev.poll, 38 | } 39 | }, 40 | plugins: [ 41 | new webpack.DefinePlugin({ 42 | 'process.env': require('../config/dev.env') 43 | }), 44 | new webpack.HotModuleReplacementPlugin(), 45 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 46 | new webpack.NoEmitOnErrorsPlugin(), 47 | // https://github.com/ampedandwired/html-webpack-plugin 48 | new HtmlWebpackPlugin({ 49 | filename: 'index.html', 50 | template: 'index.html', 51 | inject: true 52 | }), 53 | ] 54 | }) 55 | 56 | module.exports = new Promise((resolve, reject) => { 57 | portfinder.basePort = process.env.PORT || config.dev.port 58 | portfinder.getPort((err, port) => { 59 | if (err) { 60 | reject(err) 61 | } else { 62 | // publish the new Port, necessary for e2e tests 63 | process.env.PORT = port 64 | // add port to devServer config 65 | devWebpackConfig.devServer.port = port 66 | 67 | // Add FriendlyErrorsPlugin 68 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 69 | compilationSuccessInfo: { 70 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 71 | }, 72 | onErrors: config.dev.notifyOnErrors 73 | ? utils.createNotifierCallback() 74 | : undefined 75 | })) 76 | 77 | resolve(devWebpackConfig) 78 | } 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /vueblog/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 13 | 14 | const env = require('../config/prod.env') 15 | 16 | const webpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ 19 | sourceMap: config.build.productionSourceMap, 20 | extract: true, 21 | usePostCSS: true 22 | }) 23 | }, 24 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 25 | output: { 26 | path: config.build.assetsRoot, 27 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 29 | }, 30 | plugins: [ 31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 32 | new webpack.DefinePlugin({ 33 | 'process.env': env 34 | }), 35 | new UglifyJsPlugin({ 36 | uglifyOptions: { 37 | compress: { 38 | warnings: false 39 | } 40 | }, 41 | sourceMap: config.build.productionSourceMap, 42 | parallel: true 43 | }), 44 | // extract css into its own file 45 | new ExtractTextPlugin({ 46 | filename: utils.assetsPath('css/[name].[contenthash].css'), 47 | // Setting the following option to `false` will not extract CSS from codesplit chunks. 48 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 49 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 50 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 51 | allChunks: true, 52 | }), 53 | // Compress extracted CSS. We are using this plugin so that possible 54 | // duplicated CSS from different components can be deduped. 55 | new OptimizeCSSPlugin({ 56 | cssProcessorOptions: config.build.productionSourceMap 57 | ? { safe: true, map: { inline: false } } 58 | : { safe: true } 59 | }), 60 | // generate dist index.html with correct asset hash for caching. 61 | // you can customize output by editing /index.html 62 | // see https://github.com/ampedandwired/html-webpack-plugin 63 | new HtmlWebpackPlugin({ 64 | filename: config.build.index, 65 | template: 'index.html', 66 | inject: true, 67 | minify: { 68 | removeComments: true, 69 | collapseWhitespace: true, 70 | removeAttributeQuotes: true 71 | // more options: 72 | // https://github.com/kangax/html-minifier#options-quick-reference 73 | }, 74 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 75 | chunksSortMode: 'dependency' 76 | }), 77 | // keep module.id stable when vender modules does not change 78 | new webpack.HashedModuleIdsPlugin(), 79 | // enable scope hoisting 80 | new webpack.optimize.ModuleConcatenationPlugin(), 81 | // split vendor js into its own file 82 | new webpack.optimize.CommonsChunkPlugin({ 83 | name: 'vendor', 84 | minChunks (module) { 85 | // any required modules inside node_modules are extracted to vendor 86 | return ( 87 | module.resource && 88 | /\.js$/.test(module.resource) && 89 | module.resource.indexOf( 90 | path.join(__dirname, '../node_modules') 91 | ) === 0 92 | ) 93 | } 94 | }), 95 | // extract webpack runtime and module manifest to its own file in order to 96 | // prevent vendor hash from being updated whenever app bundle is updated 97 | new webpack.optimize.CommonsChunkPlugin({ 98 | name: 'manifest', 99 | minChunks: Infinity 100 | }), 101 | // This instance extracts shared chunks from code splitted chunks and bundles them 102 | // in a separate chunk, similar to the vendor chunk 103 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 104 | new webpack.optimize.CommonsChunkPlugin({ 105 | name: 'app', 106 | async: 'vendor-async', 107 | children: true, 108 | minChunks: 3 109 | }), 110 | 111 | // copy custom static assets 112 | new CopyWebpackPlugin([ 113 | { 114 | from: path.resolve(__dirname, '../static'), 115 | to: config.build.assetsSubDirectory, 116 | ignore: ['.*'] 117 | } 118 | ]) 119 | ] 120 | }) 121 | 122 | if (config.build.productionGzip) { 123 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 124 | 125 | webpackConfig.plugins.push( 126 | new CompressionWebpackPlugin({ 127 | asset: '[path].gz[query]', 128 | algorithm: 'gzip', 129 | test: new RegExp( 130 | '\\.(' + 131 | config.build.productionGzipExtensions.join('|') + 132 | ')$' 133 | ), 134 | threshold: 10240, 135 | minRatio: 0.8 136 | }) 137 | ) 138 | } 139 | 140 | if (config.build.bundleAnalyzerReport) { 141 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 142 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 143 | } 144 | 145 | module.exports = webpackConfig 146 | -------------------------------------------------------------------------------- /vueblog/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /vueblog/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.2.7 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: { 14 | '/': { 15 | target: 'http://localhost:8081', 16 | changeOrigin: true, 17 | pathRewrite: { 18 | '^/': '' 19 | } 20 | } 21 | }, 22 | 23 | // Various Dev Server settings 24 | host: 'localhost', // can be overwritten by process.env.HOST 25 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 26 | autoOpenBrowser: false, 27 | errorOverlay: true, 28 | notifyOnErrors: true, 29 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 30 | 31 | 32 | /** 33 | * Source Maps 34 | */ 35 | 36 | // https://webpack.js.org/configuration/devtool/#development 37 | devtool: 'eval-source-map', 38 | 39 | // If you have problems debugging vue-files in devtools, 40 | // set this to false - it *may* help 41 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 42 | cacheBusting: true, 43 | 44 | // CSS Sourcemaps off by default because relative paths are "buggy" 45 | // with this option, according to the CSS-Loader README 46 | // (https://github.com/webpack/css-loader#sourcemaps) 47 | // In our experience, they generally work as expected, 48 | // just be aware of this issue when enabling this option. 49 | cssSourceMap: false, 50 | }, 51 | 52 | build: { 53 | // Template for index.html 54 | index: path.resolve(__dirname, '../dist/index.html'), 55 | 56 | // Paths 57 | assetsRoot: path.resolve(__dirname, '../dist'), 58 | assetsSubDirectory: 'static', 59 | assetsPublicPath: '/', 60 | 61 | /** 62 | * Source Maps 63 | */ 64 | 65 | productionSourceMap: true, 66 | // https://webpack.js.org/configuration/devtool/#production 67 | devtool: '#source-map', 68 | 69 | // Gzip off by default as many popular static hosts such as 70 | // Surge or Netlify already gzip all static assets for you. 71 | // Before setting to `true`, make sure to: 72 | // npm install --save-dev compression-webpack-plugin 73 | productionGzip: false, 74 | productionGzipExtensions: ['js', 'css'], 75 | 76 | // Run the build command with an extra argument to 77 | // View the bundle analyzer report after build finishes: 78 | // `npm run build --report` 79 | // Set to `true` or `false` to always turn it on or off 80 | bundleAnalyzerReport: process.env.npm_config_report 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /vueblog/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /vueblog/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | V部落 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /vueblog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vueblog", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "sang", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js" 11 | }, 12 | "dependencies": { 13 | "axios": "^0.17.1", 14 | "element-ui": "^2.0.8", 15 | "font-awesome": "^4.7.0", 16 | "mavon-editor": "^2.4.13", 17 | "vue": "^2.5.2", 18 | "vue-echarts": "^2.6.0", 19 | "vue-router": "^3.0.1" 20 | }, 21 | "devDependencies": { 22 | "autoprefixer": "^7.1.2", 23 | "babel-core": "^6.22.1", 24 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 25 | "babel-loader": "^7.1.1", 26 | "babel-plugin-syntax-jsx": "^6.18.0", 27 | "babel-plugin-transform-runtime": "^6.22.0", 28 | "babel-plugin-transform-vue-jsx": "^3.5.0", 29 | "babel-preset-env": "^1.3.2", 30 | "babel-preset-stage-2": "^6.22.0", 31 | "chalk": "^2.0.1", 32 | "copy-webpack-plugin": "^4.0.1", 33 | "css-loader": "^0.28.7", 34 | "extract-text-webpack-plugin": "^3.0.0", 35 | "file-loader": "^1.1.4", 36 | "friendly-errors-webpack-plugin": "^1.6.1", 37 | "html-webpack-plugin": "^2.30.1", 38 | "node-notifier": "^5.1.2", 39 | "node-sass": "^4.7.2", 40 | "optimize-css-assets-webpack-plugin": "^3.2.0", 41 | "ora": "^1.2.0", 42 | "portfinder": "^1.0.13", 43 | "postcss-import": "^11.0.0", 44 | "postcss-loader": "^2.0.8", 45 | "rimraf": "^2.6.0", 46 | "sass-loader": "^6.0.6", 47 | "semver": "^5.3.0", 48 | "shelljs": "^0.7.6", 49 | "style-loader": "^0.19.1", 50 | "stylus-loader": "^3.0.1", 51 | "uglifyjs-webpack-plugin": "^1.1.1", 52 | "url-loader": "^0.5.8", 53 | "vue-loader": "^13.3.0", 54 | "vue-style-loader": "^3.0.3", 55 | "vue-template-compiler": "^2.5.2", 56 | "webpack": "^3.6.0", 57 | "webpack-bundle-analyzer": "^2.9.0", 58 | "webpack-dev-server": "^2.9.1", 59 | "webpack-merge": "^4.1.0" 60 | }, 61 | "engines": { 62 | "node": ">= 4.0.0", 63 | "npm": ">= 3.0.0" 64 | }, 65 | "browserslist": [ 66 | "> 1%", 67 | "last 2 versions", 68 | "not ie <= 8" 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /vueblog/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /vueblog/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/vueblog/src/assets/logo.png -------------------------------------------------------------------------------- /vueblog/src/components/ArticleList.vue: -------------------------------------------------------------------------------- 1 | 27 | 60 | 79 | -------------------------------------------------------------------------------- /vueblog/src/components/BlogCfg.vue: -------------------------------------------------------------------------------- 1 | 20 | 66 | -------------------------------------------------------------------------------- /vueblog/src/components/BlogDetail.vue: -------------------------------------------------------------------------------- 1 | 30 | 62 | -------------------------------------------------------------------------------- /vueblog/src/components/BlogTable.vue: -------------------------------------------------------------------------------- 1 | 11 | 86 | 87 | 245 | -------------------------------------------------------------------------------- /vueblog/src/components/CateMana.vue: -------------------------------------------------------------------------------- 1 | 56 | 208 | 227 | -------------------------------------------------------------------------------- /vueblog/src/components/DataCharts.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 10 | 82 | -------------------------------------------------------------------------------- /vueblog/src/components/Home.bak.vue: -------------------------------------------------------------------------------- 1 | 53 | 87 | 123 | -------------------------------------------------------------------------------- /vueblog/src/components/Home.vue: -------------------------------------------------------------------------------- 1 | 58 | 99 | 143 | -------------------------------------------------------------------------------- /vueblog/src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 17 | 64 | 87 | -------------------------------------------------------------------------------- /vueblog/src/components/PostArticle.vue: -------------------------------------------------------------------------------- 1 | 50 | 189 | 234 | -------------------------------------------------------------------------------- /vueblog/src/components/UserMana.vue: -------------------------------------------------------------------------------- 1 | 77 | 248 | -------------------------------------------------------------------------------- /vueblog/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | import router from './router' 4 | import ElementUI from 'element-ui' 5 | import 'element-ui/lib/theme-chalk/index.css' 6 | // import './styles/element-variables.scss' 7 | import 'font-awesome/css/font-awesome.min.css' 8 | import './utils/filter_utils.js' 9 | 10 | Vue.use(ElementUI) 11 | Vue.config.productionTip = false 12 | window.bus = new Vue(); 13 | new Vue({ 14 | el: '#app', 15 | router, 16 | template: '', 17 | components: {App} 18 | }) 19 | -------------------------------------------------------------------------------- /vueblog/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Login from '@/components/Login' 4 | import Home from '@/components/Home' 5 | import ArticleList from '@/components/ArticleList' 6 | import CateMana from '@/components/CateMana' 7 | import DataCharts from '@/components/DataCharts' 8 | import PostArticle from '@/components/PostArticle' 9 | import UserMana from '@/components/UserMana' 10 | import BlogDetail from '@/components/BlogDetail' 11 | 12 | Vue.use(Router) 13 | 14 | export default new Router({ 15 | routes: [ 16 | { 17 | path: '/', 18 | name: '登录', 19 | hidden: true, 20 | component: Login 21 | }, { 22 | path: '/home', 23 | name: '', 24 | component: Home, 25 | hidden: true 26 | }, { 27 | path: '/home', 28 | component: Home, 29 | name: '文章管理', 30 | iconCls: 'fa fa-file-text-o', 31 | children: [ 32 | { 33 | path: '/articleList', 34 | name: '文章列表', 35 | component: ArticleList, 36 | meta: { 37 | keepAlive: true 38 | } 39 | }, { 40 | path: '/postArticle', 41 | name: '发表文章', 42 | component: PostArticle, 43 | meta: { 44 | keepAlive: false 45 | } 46 | }, { 47 | path: '/blogDetail', 48 | name: '博客详情', 49 | component: BlogDetail, 50 | hidden: true, 51 | meta: { 52 | keepAlive: false 53 | } 54 | }, { 55 | path: '/editBlog', 56 | name: '编辑博客', 57 | component: PostArticle, 58 | hidden: true, 59 | meta: { 60 | keepAlive: false 61 | } 62 | } 63 | ] 64 | }, { 65 | path: '/home', 66 | component: Home, 67 | name: '用户管理', 68 | children: [ 69 | { 70 | path: '/user', 71 | iconCls: 'fa fa-user-o', 72 | name: '用户管理', 73 | component: UserMana 74 | } 75 | ] 76 | }, { 77 | path: '/home', 78 | component: Home, 79 | name: '栏目管理', 80 | children: [ 81 | { 82 | path: '/cateMana', 83 | iconCls: 'fa fa-reorder', 84 | name: '栏目管理', 85 | component: CateMana 86 | } 87 | ] 88 | }, { 89 | path: '/home', 90 | component: Home, 91 | name: '数据统计', 92 | iconCls: 'fa fa-bar-chart', 93 | children: [ 94 | { 95 | path: '/charts', 96 | iconCls: 'fa fa-bar-chart', 97 | name: '数据统计', 98 | component: DataCharts 99 | } 100 | ] 101 | } 102 | ] 103 | }) 104 | -------------------------------------------------------------------------------- /vueblog/src/styles/element-variables.scss: -------------------------------------------------------------------------------- 1 | /* 改变主题色变量 */ 2 | //$--color-primary: #000000; 3 | // 4 | ///* 改变 icon 字体路径变量,必需 */ 5 | //$--font-path: '~element-ui/lib/theme-chalk/fonts'; 6 | // 7 | //@import "~element-ui/packages/theme-chalk/src/index"; 8 | -------------------------------------------------------------------------------- /vueblog/src/utils/api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | let base = ''; 4 | export const postRequest = (url, params) => { 5 | return axios({ 6 | method: 'post', 7 | url: `${base}${url}`, 8 | data: params, 9 | transformRequest: [function (data) { 10 | // Do whatever you want to transform the data 11 | let ret = '' 12 | for (let it in data) { 13 | ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&' 14 | } 15 | return ret 16 | }], 17 | headers: { 18 | 'Content-Type': 'application/x-www-form-urlencoded' 19 | } 20 | }); 21 | } 22 | export const uploadFileRequest = (url, params) => { 23 | return axios({ 24 | method: 'post', 25 | url: `${base}${url}`, 26 | data: params, 27 | headers: { 28 | 'Content-Type': 'multipart/form-data' 29 | } 30 | }); 31 | } 32 | export const putRequest = (url, params) => { 33 | return axios({ 34 | method: 'put', 35 | url: `${base}${url}`, 36 | data: params, 37 | transformRequest: [function (data) { 38 | let ret = '' 39 | for (let it in data) { 40 | ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&' 41 | } 42 | return ret 43 | }], 44 | headers: { 45 | 'Content-Type': 'application/x-www-form-urlencoded' 46 | } 47 | }); 48 | } 49 | export const deleteRequest = (url) => { 50 | return axios({ 51 | method: 'delete', 52 | url: `${base}${url}` 53 | }); 54 | } 55 | export const getRequest = (url,params) => { 56 | return axios({ 57 | method: 'get', 58 | data:params, 59 | transformRequest: [function (data) { 60 | let ret = '' 61 | for (let it in data) { 62 | ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&' 63 | } 64 | return ret 65 | }], 66 | headers: { 67 | 'Content-Type': 'application/x-www-form-urlencoded' 68 | }, 69 | url: `${base}${url}` 70 | }); 71 | } 72 | -------------------------------------------------------------------------------- /vueblog/src/utils/filter_utils.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | Vue.filter("formatDate", function formatDate(value) { 3 | var date = new Date(value); 4 | var year = date.getFullYear(); 5 | var month = date.getMonth() + 1; 6 | var day = date.getDate(); 7 | if (month < 10) { 8 | month = "0" + month; 9 | } 10 | if (day < 10) { 11 | day = "0" + day; 12 | } 13 | return year + "-" + month + "-" + day; 14 | }); 15 | Vue.filter("formatDateTime", function formatDateTime(value) { 16 | var date = new Date(value); 17 | var year = date.getFullYear(); 18 | var month = date.getMonth() + 1; 19 | var day = date.getDate(); 20 | var hours = date.getHours(); 21 | var minutes = date.getMinutes(); 22 | if (month < 10) { 23 | month = "0" + month; 24 | } 25 | if (day < 10) { 26 | day = "0" + day; 27 | } 28 | return year + "-" + month + "-" + day + " " + hours + ":" + minutes; 29 | }); 30 | 31 | -------------------------------------------------------------------------------- /vueblog/src/utils/utils.js: -------------------------------------------------------------------------------- 1 | export const isNotNullORBlank = (...args)=> { 2 | for (var i = 0; i < args.length; i++) { 3 | var argument = args[i]; 4 | if (argument == null || argument == '' || argument == undefined) { 5 | return false; 6 | } 7 | } 8 | return true; 9 | } 10 | -------------------------------------------------------------------------------- /vueblog/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogTech/VBlog/3d369ef49f166e6e725815d3a42a2d8472e6dac4/vueblog/static/.gitkeep --------------------------------------------------------------------------------