从"Blog"仓库中分离出来
This commit is contained in:
330
index/src/app/pages/article/article.component.css
Normal file
330
index/src/app/pages/article/article.component.css
Normal file
@@ -0,0 +1,330 @@
|
||||
/* markdown Css style */
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.list-group li {
|
||||
/* margin-top: 10px;*/
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.arUl {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.arUl li {
|
||||
margin-right: 12px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.arUl li i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.arTypeOriginal {
|
||||
background: #5eb95e;
|
||||
}
|
||||
|
||||
.arTypeReprint {
|
||||
background: #F37B1D;
|
||||
}
|
||||
|
||||
.arType {
|
||||
color: #ffffff;
|
||||
font-size: 0.8em;
|
||||
padding: 2px 6px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.article-titlea {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
.article-meta {
|
||||
width: 100%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.article-meta a {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.article-road {
|
||||
margin-left: 2%;
|
||||
}
|
||||
|
||||
.secd {
|
||||
margin-left: 30%;
|
||||
}
|
||||
|
||||
#title {
|
||||
word-break: break-all;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font-size: 2em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.article-text {
|
||||
word-break: break-all
|
||||
}
|
||||
|
||||
.article-tag {
|
||||
margin-left: 5%;
|
||||
}
|
||||
|
||||
|
||||
/*文章底部的tag标签*/
|
||||
|
||||
.tag {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.article-bAnda {
|
||||
width: 90%;
|
||||
margin-top: 2%;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
}
|
||||
|
||||
.articlePublishDate,
|
||||
.originalAuthor,
|
||||
.categories {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.article-input-textarea {
|
||||
border-color: #aaaaaa;
|
||||
border-width: 2px;
|
||||
border-radius: 10px;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
max-height: 700px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.comment-submit {
|
||||
float: right;
|
||||
color: white;
|
||||
background: #3bb4f2;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
padding: 3px 8px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.comment-list {
|
||||
margin: 50px 10% 20px;
|
||||
}
|
||||
|
||||
.comment-input {
|
||||
border-color: #ececec;
|
||||
border-width: 1px;
|
||||
outline: none;
|
||||
border-radius: 4px;
|
||||
margin-left: 10px;
|
||||
margin-top: 21px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.article-comment {
|
||||
margin: 0 10%;
|
||||
}
|
||||
|
||||
.comment-input-coverText {
|
||||
text-align: center;
|
||||
background-color: white;
|
||||
margin: 5px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.input-area {
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.buttonDisable {
|
||||
pointer-events: none
|
||||
}
|
||||
|
||||
.articleButton {
|
||||
background: none;
|
||||
border: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.articleButton:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.commentUser {
|
||||
background: #5eb95e;
|
||||
padding: 3px 8px;
|
||||
border-radius: 3px;
|
||||
color: white
|
||||
}
|
||||
|
||||
.commentItem {
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.loading {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.loading img {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%)
|
||||
}
|
||||
|
||||
#pagination {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.tag {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.tag i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.article-last,
|
||||
.article-next {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#over {
|
||||
display: block;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
line-height: 80px;
|
||||
margin: 15px auto;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 2em;
|
||||
background: #3bb4f2;
|
||||
color: white
|
||||
}
|
||||
|
||||
#copyright {
|
||||
width: 90%;
|
||||
margin: 15px 5%;
|
||||
border-left: 5px solid #FF1700;
|
||||
padding: 10px;
|
||||
background: #ececec
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
.comment {
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
#content {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#content p {
|
||||
word-break: break-all
|
||||
}
|
||||
|
||||
button {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 0 5%;
|
||||
}
|
||||
|
||||
.noToc{
|
||||
width: 65% !important;
|
||||
margin: 0 auto !important;
|
||||
height: auto;
|
||||
position: static !important;
|
||||
border-radius: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 768px) {
|
||||
|
||||
.article-content {
|
||||
left: 400px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin-right: 500px;
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.article-toc-main{
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
position: fixed;
|
||||
left: 40px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 320px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#article-toc{
|
||||
max-height: 700px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.article-content {
|
||||
width: 100%;
|
||||
margin-top: -80px;
|
||||
padding-top: 60px;
|
||||
margin-right:0;
|
||||
border-radius: 0;
|
||||
margin-left: 0!important;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.article-toc-main{
|
||||
position:relative;
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
}
|
||||
.noToc{
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
}
|
||||
149
index/src/app/pages/article/article.component.html
Normal file
149
index/src/app/pages/article/article.component.html
Normal file
@@ -0,0 +1,149 @@
|
||||
<ng-template [ngIf]="article">
|
||||
<nz-spin [nzSpinning]="!loadOk">
|
||||
|
||||
|
||||
<div class="article-content am-article" [ngClass]="{'noToc':!showToc}">
|
||||
<div style="text-align: center;word-break: break-all">
|
||||
<h1 class="article-title" id="title">{{article.title}}</h1>
|
||||
</div>
|
||||
<br>
|
||||
<ul class="arUl">
|
||||
<li>
|
||||
<span class="arType"
|
||||
[ngClass]="{'arTypeOriginal': article.original,'arTypeReprint':!article.original}">{{article.original ? "原创" : "转载"}}</span>
|
||||
</li>
|
||||
<li><i nz-icon nzType="calendar" nzTheme="outline"></i>{{article.publishDateFormat}}</li>
|
||||
<li><i nz-icon nzType="user" nzTheme="outline"></i>{{article.authorName}}</li>
|
||||
<li><i nz-icon nzType="folder" nzTheme="outline"></i>
|
||||
<a [routerLink]="['/category']" [queryParams]="{'name':article.category}">
|
||||
<span>{{article.category}}</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
<div class="article-text am-text-break am-article-bd">
|
||||
<!-- <div id="content" [innerHTML]="article.htmlContent">
|
||||
</div> -->
|
||||
<div class="article-toc-main" *ngIf="showToc">
|
||||
<!-- fixme: 不同文章切换时 无法生效 -->
|
||||
<span><strong>目录:</strong></span><br>
|
||||
<div id="article-toc" class="markdown-body editormd-preview-container">
|
||||
</div>
|
||||
</div>
|
||||
<div id="content">
|
||||
<textarea></textarea>
|
||||
</div>
|
||||
|
||||
<span id="over">over</span>
|
||||
</div>
|
||||
<!-- 文章版权 -->
|
||||
<div id="copyright">
|
||||
<p>本文作者:{{article.authorName}} </p>
|
||||
<p>{{article.original ? "本文" : "原文"}}链接:{{article.original ? copyRightUrl : article.url}}</p>
|
||||
<p>版权声明:转载请注明出处</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="article-tag" id="tag">
|
||||
<!-- TODO -->
|
||||
<span *ngFor="let item of (article.tags||'')" class="tag">
|
||||
<i nz-icon nzType="tag" nzTheme="fill"></i>
|
||||
<a class="tag" [routerLink]="['/tag']" [queryParams]="{name:item}">{{item}}</a>
|
||||
</span>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="article-bAnda">
|
||||
<span class="article-last">
|
||||
<button (click)="toArticle(article.nextArticleId)" class="articleButton"
|
||||
[ngClass]="{'buttonDisable': article.nextArticleId==-1}">
|
||||
<i nz-icon nzType="caret-up"
|
||||
nzTheme="outline"></i> 上一篇文章:{{article.nextArticleTitle}}</button>
|
||||
</span>
|
||||
<span class="article-next">
|
||||
<button (click)="toArticle(article.preArticleId)" class="articleButton"
|
||||
[ngClass]="{'buttonDisable': article.preArticleId==-1}">
|
||||
<i nz-icon nzType="caret-down"
|
||||
nzTheme="outline"></i> 下一篇文章篇文章:{{article.preArticleTitle}}</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<span *ngIf="!userService.userInfo" style="display: block;text-align: center">若要评论,请先
|
||||
<a style="color: blue !important;text-decoration: underline"
|
||||
(click)="userService.showModal('login')">登录</a>哟~~~~</span>
|
||||
<nz-comment *ngIf="userService.userInfo" style="width: 60%;margin:15px auto">
|
||||
<nz-comment-content>
|
||||
<nz-form-item>
|
||||
<textarea [(ngModel)]="comment4submit" nz-input style="height: 130px;width: 100%;"></textarea>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<button nz-button nzType="primary" [disabled]="!comment4submit" (click)="submitComment()">评论</button>
|
||||
</nz-form-item>
|
||||
</nz-comment-content>
|
||||
</nz-comment>
|
||||
|
||||
<!-- 展示 -->
|
||||
<nz-card id="leaveMsgs" [nzLoading]="!commentService.commentPage.pageSize">
|
||||
<ng-template [ngIf]="commentService.commentPage.pageSize">
|
||||
<nz-comment *ngFor="let comment of commentService.commentPage.list; let i = index"
|
||||
[nzAuthor]="comment.authorName"
|
||||
[nzDatetime]="comment.date">
|
||||
<nz-avatar nz-comment-avatar nzIcon="user" [nzSrc]="comment.authorAvatarImgUrl"></nz-avatar>
|
||||
<nz-comment-content class="comment">
|
||||
<p>
|
||||
{{comment.content}}
|
||||
</p>
|
||||
</nz-comment-content>
|
||||
<nz-comment-action><span (click)="replyTo(comment.id,comment.authorName,i)">
|
||||
<i nz-icon nzType="message" nzTheme="fill"></i> 回复</span>
|
||||
</nz-comment-action>
|
||||
|
||||
<!-- 二级评论 -->
|
||||
<ng-container *ngIf="comment.child && comment.child.length">
|
||||
|
||||
<nz-comment *ngFor="let secComment of comment.child" [nzAuthor]="secComment.authorName"
|
||||
[nzDatetime]="secComment.date">
|
||||
<nz-avatar nz-comment-avatar nzIcon="user" [nzSrc]="secComment.authorAvatarImgUrl"></nz-avatar>
|
||||
<nz-comment-content class="comment">
|
||||
<p>
|
||||
{{secComment.content}}
|
||||
</p>
|
||||
</nz-comment-content>
|
||||
<!-- <nz-comment-action><span (click)="replyTo(secComment.id,secComment.authorName,i)">
|
||||
<i nz-icon nzType="message" nzTheme="fill"></i> 回复</span>
|
||||
</nz-comment-action> -->
|
||||
|
||||
</nz-comment>
|
||||
|
||||
</ng-container>
|
||||
<!-- 二级评论的回复框 -->
|
||||
<nz-comment *ngIf="responseComment.pid!=null&&relyIndex==i">
|
||||
<nz-comment>
|
||||
<nz-comment-content>
|
||||
<nz-form-item>
|
||||
<textarea nz-input [(ngModel)]="responseComment.content"
|
||||
style="height: 130px;width: 80%;padding: 10px;border-radius: 5px;"></textarea>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<button nz-button nzType="primary" [disabled]="!responseComment.content" (click)="reply()"> 回复
|
||||
</button>
|
||||
<button nz-button nzType="default" style="margin-left: 15px;" (click)="responseComment.pid=null"> 取消
|
||||
</button>
|
||||
</nz-form-item>
|
||||
</nz-comment-content>
|
||||
</nz-comment>
|
||||
</nz-comment>
|
||||
</nz-comment>
|
||||
|
||||
<nz-pagination style="text-align: center" [nzPageIndex]="pageNum"
|
||||
[nzTotal]="commentService.commentPage.total"
|
||||
[nzPageSize]="pageSize" [nzHideOnSinglePage]="true"
|
||||
(nzPageIndexChange)="toPage($event)"></nz-pagination>
|
||||
|
||||
</ng-template>
|
||||
</nz-card>
|
||||
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
</nz-spin>
|
||||
</ng-template>
|
||||
25
index/src/app/pages/article/article.component.spec.ts
Normal file
25
index/src/app/pages/article/article.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ArticleComponent } from './article.component';
|
||||
|
||||
describe('ArticleComponent', () => {
|
||||
let component: ArticleComponent;
|
||||
let fixture: ComponentFixture<ArticleComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ArticleComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ArticleComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
228
index/src/app/pages/article/article.component.ts
Normal file
228
index/src/app/pages/article/article.component.ts
Normal file
@@ -0,0 +1,228 @@
|
||||
import {Component, OnInit, ViewEncapsulation} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {Router} from '@angular/router';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {Location} from '@angular/common';
|
||||
import {NzMessageService} from 'ng-zorro-antd';
|
||||
import {ArticleService} from '../../services/article/article.service';
|
||||
import {CommentService} from '../../services/comment/comment.service';
|
||||
import {UserService} from '../../services/user/user.service';
|
||||
import {CommentReq} from '../../class/commentReq';
|
||||
import {Article} from '../../class/article';
|
||||
|
||||
declare var editormd;
|
||||
declare var $;
|
||||
|
||||
@Component({
|
||||
selector: 'app-article',
|
||||
templateUrl: './article.component.html',
|
||||
styleUrls: ['./article.component.css']
|
||||
})
|
||||
export class ArticleComponent implements OnInit {
|
||||
|
||||
constructor(private articleService: ArticleService,
|
||||
private routerinfo: ActivatedRoute,
|
||||
private router: Router,
|
||||
private message: NzMessageService,
|
||||
private titleService: Title,
|
||||
private location: Location,
|
||||
public commentService: CommentService,
|
||||
public userService: UserService) {
|
||||
}
|
||||
|
||||
copyRightUrl: string;
|
||||
|
||||
public pageNum: number = 1;
|
||||
public pageSize: number = 10;
|
||||
|
||||
public articleId: number;
|
||||
|
||||
public article: Article = new Article();
|
||||
|
||||
public comment4submit: string = '';
|
||||
|
||||
loadOk: boolean = false; // 文章的加载
|
||||
|
||||
loading: boolean = true; // 评论的加载
|
||||
|
||||
relyIndex: number;
|
||||
|
||||
public responseComment: CommentReq;
|
||||
|
||||
public showToc: boolean = true;
|
||||
|
||||
/**
|
||||
* editor.md preview
|
||||
*
|
||||
*/
|
||||
public preview(markdownContent) {
|
||||
editormd.markdownToHTML('content', {
|
||||
markdown: markdownContent,
|
||||
// htmlDecode : true, // 开启 HTML 标签解析,为了安全性,默认不开启
|
||||
htmlDecode: 'style,script,iframe', // you can filter tags decode
|
||||
toc : true,
|
||||
tocm : true, // Using [TOCM]
|
||||
tocContainer : '#article-toc', // 自定义 ToC 容器层
|
||||
// gfm : false,
|
||||
tocDropdown: false,
|
||||
// markdownSourceCode : true, // 是否保留 Markdown 源码,即是否删除保存源码的 Textarea 标签
|
||||
emoji: true,
|
||||
taskList: true,
|
||||
// tex : true, //科学公式 默认不解析
|
||||
flowChart: true, // 默认不解析
|
||||
sequenceDiagram: true, // 默认不解析
|
||||
});
|
||||
this.checkALink();
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
this.articleId = +this.routerinfo.snapshot.paramMap.get('id');
|
||||
this.responseComment = new CommentReq(true);
|
||||
this.getArticle();
|
||||
this.getPageComment();
|
||||
this.responseComment.articleID = this.articleId;
|
||||
this.copyRightUrl = location.href;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并处理文章
|
||||
*/
|
||||
getArticle() {
|
||||
this.articleService.getArticleById(this.articleId).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
// 清空远先的markdown 内容
|
||||
document.getElementById('content').innerHTML = '';
|
||||
this.article = data.result;
|
||||
this.preview(data.result.mdContent);
|
||||
window.scrollTo(0, 0);
|
||||
this.titleService.setTitle(data.result.title);
|
||||
// 修改url栏的地址
|
||||
this.location.replaceState('article/' + data.result.id);
|
||||
this.copyRightUrl = location.href;
|
||||
this.loadOk = true;
|
||||
} else if (data.code === 201) {
|
||||
// 文章不存在
|
||||
this.router.navigateByUrl('404');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 跳转id文章
|
||||
* @param id 文章id
|
||||
*/
|
||||
toArticle(id: number) {
|
||||
if (id == null || id <= 0) {
|
||||
return;
|
||||
}
|
||||
this.articleId = id;
|
||||
this.loadOk = false;
|
||||
this.getArticle();
|
||||
this.getPageComment();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 评论调到某一页
|
||||
* @param a 页码数
|
||||
*/
|
||||
toPage(a: number) {
|
||||
if (a === this.pageNum) {
|
||||
return;
|
||||
}
|
||||
this.pageNum = a;
|
||||
this.getPageComment();
|
||||
}
|
||||
|
||||
// 评论
|
||||
getPageComment() {
|
||||
this.commentService.getPageComment(this.articleId, this.pageNum, this.pageSize).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.loading = false;
|
||||
this.commentService.getResponseComment();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 一级评论的创建
|
||||
*/
|
||||
submitComment() {
|
||||
if (this.comment4submit == null || this.comment4submit === '') {
|
||||
this.message.info('内容不能为空');
|
||||
return;
|
||||
}
|
||||
const submitBody: CommentReq = new CommentReq(true);
|
||||
submitBody.content = this.comment4submit;
|
||||
submitBody.articleID = this.articleId;
|
||||
submitBody.pid = -1;
|
||||
|
||||
this.commentService.submitComment(submitBody);
|
||||
this.comment4submit = null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文本输入框的过程
|
||||
* @param id 父评论的id
|
||||
* @param name 富评论的作者名
|
||||
* @param index 索引 便于显示输入框
|
||||
*/
|
||||
replyTo(id, name, index) {
|
||||
if (!this.userService.userInfo) {
|
||||
this.message.info('请先登录哟~~~');
|
||||
return;
|
||||
}
|
||||
this.responseComment.pid = id;
|
||||
this.responseComment.content = ' @' + name + ' ';
|
||||
this.relyIndex = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复的数据提交
|
||||
*/
|
||||
reply() {
|
||||
if (this.responseComment.content == null || this.responseComment.content === '') {
|
||||
this.message.info('内容不能为空');
|
||||
return;
|
||||
}
|
||||
if (this.responseComment.pid == null) {
|
||||
this.message.error('非法操作');
|
||||
}
|
||||
this.commentService.rely(this.responseComment).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
if (this.commentService.commentPage.list[this.relyIndex].child == null) {
|
||||
this.commentService.commentPage.list[this.relyIndex].child = [];
|
||||
}
|
||||
this.commentService.commentPage.list[this.relyIndex].child.unshift(data.result);
|
||||
this.responseComment.content = null;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 曲线救国 重新设置a的href值
|
||||
checkALink() {
|
||||
const as = document.getElementsByTagName('a');
|
||||
let hrefStr: string = null ;
|
||||
let count: number = 0;
|
||||
// tslint:disable-next-line:prefer-for-of
|
||||
for (let i = 0; i < as.length; i++) {
|
||||
hrefStr = decodeURI(as[i].href);
|
||||
// console.log(as[i].getAttribute('level'));
|
||||
if (as[i].getAttribute('level') != null) {
|
||||
// 截取锚点名称
|
||||
const anchorName: string = hrefStr.substr(hrefStr.indexOf('#'), hrefStr.length);
|
||||
// 重新获取url 并拼接锚点名称
|
||||
as[i].href = location.origin + location.pathname + anchorName;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count === 0) {
|
||||
this.showToc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
index/src/app/pages/categories/category.component.css
Normal file
65
index/src/app/pages/categories/category.component.css
Normal file
@@ -0,0 +1,65 @@
|
||||
#main{
|
||||
width:60%;
|
||||
margin:0 auto;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
background: #ffffff;
|
||||
}
|
||||
#category{
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ececec;
|
||||
box-shadow: 5px 5px 5px #888888;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#detail{
|
||||
clear: both;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.singleTag{
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
line-height: 15px;
|
||||
margin: 5px 10px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
border: 1px solid #000000;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
ul{
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.art{
|
||||
padding: 8px;
|
||||
border: 1px solid #ececec;
|
||||
box-shadow: 5px 5px 5px #aaaaaa;
|
||||
margin: 20px 0 ;
|
||||
}
|
||||
.title:hover{
|
||||
animation: 1s move ;
|
||||
}
|
||||
@keyframes move{
|
||||
from{
|
||||
margin-left: 0;
|
||||
}
|
||||
to{
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.active{
|
||||
background: #6DAB90;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width:768px){
|
||||
|
||||
#main{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
25
index/src/app/pages/categories/category.component.html
Normal file
25
index/src/app/pages/categories/category.component.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<div id="main">
|
||||
|
||||
<div id="category">
|
||||
<ul *ngIf="categoryService.categories">
|
||||
<li *ngFor="let category of categoryService.categories" (click)="changeCategory(category.name)">
|
||||
<span class="singleTag" [ngClass]="{active:currentCategoryName==category.name}"
|
||||
[nzTitle]="'此分类有'+(category.articles.split(',').length-1)+'篇文章'" nzPlacement="topCenter" nz-tooltip>
|
||||
<i nz-icon nzType="folder" nzTheme="fill"></i> {{category.name}}</span></li>
|
||||
</ul>
|
||||
<h2 *ngIf="!categoryService.categories">暂时没有分类</h2>
|
||||
</div>
|
||||
|
||||
<ul id="detail" *ngIf="currentArticleList">
|
||||
<li *ngFor="let article of currentArticleList.list">
|
||||
<div class="art">
|
||||
<h2><a [routerLink]="'/article/'+article.id" class="title">{{article.title}}</a></h2>
|
||||
<hr>
|
||||
<div> {{article.summary}}</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div *ngIf="!currentArticleList">
|
||||
<h2 style="text-align: center">该分类暂无文章</h2>
|
||||
</div>
|
||||
</div>
|
||||
25
index/src/app/pages/categories/category.component.spec.ts
Normal file
25
index/src/app/pages/categories/category.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CategoriesComponent } from './category.component';
|
||||
|
||||
describe('CategoriesComponent', () => {
|
||||
let component: CategoriesComponent;
|
||||
let fixture: ComponentFixture<CategoriesComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ CategoriesComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CategoriesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
71
index/src/app/pages/categories/category.component.ts
Normal file
71
index/src/app/pages/categories/category.component.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {Location} from '@angular/common';
|
||||
import {CategoryService} from '../../services/category/category.service';
|
||||
import {ArticleService} from '../../services/article/article.service';
|
||||
import {Page} from '../../class/page';
|
||||
import {Article} from '../../class/article';
|
||||
|
||||
@Component({
|
||||
selector: 'app-categories',
|
||||
templateUrl: './category.component.html',
|
||||
styleUrls: ['./category.component.css']
|
||||
})
|
||||
export class CategoryComponent implements OnInit {
|
||||
|
||||
constructor(private routerinfo: ActivatedRoute,
|
||||
private titleService: Title,
|
||||
private location: Location,
|
||||
public categoryService: CategoryService,
|
||||
public articleService: ArticleService) {
|
||||
titleService.setTitle('小海博客|分类');
|
||||
}
|
||||
|
||||
// 当前timeliness展示的分类
|
||||
public currentCategoryName: string;
|
||||
public currentArticleList: Page<Article>;
|
||||
|
||||
ngOnInit() {
|
||||
this.currentCategoryName = this.routerinfo.snapshot.queryParams.name;
|
||||
if (this.categoryService.categories == null) {
|
||||
this.categoryService.getAllCategory().subscribe((data) => {
|
||||
// 有分类数据
|
||||
if (data.code === 0 && data.result.length > 0) {
|
||||
// 是否更具url的参数获取数据
|
||||
if (this.currentCategoryName == null) {
|
||||
this.currentCategoryName = data.result[0].name;
|
||||
}
|
||||
this.getArticle();
|
||||
}
|
||||
});
|
||||
} else if (this.currentCategoryName == null) {
|
||||
this.currentCategoryName = this.categoryService.categories[0].name;
|
||||
}
|
||||
this.getArticle();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 切换 分类
|
||||
* @param name 分类名称
|
||||
*/
|
||||
changeCategory(name: string) {
|
||||
if (this.currentCategoryName === name) {
|
||||
return;
|
||||
}
|
||||
this.currentCategoryName = name;
|
||||
this.getArticle();
|
||||
this.location.replaceState('category', '?name=' + name);
|
||||
}
|
||||
|
||||
|
||||
getArticle() {
|
||||
this.articleService.getArticleByCategory(this.currentCategoryName).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.currentArticleList = data.result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#main{
|
||||
width: 60%;
|
||||
height: 50px;
|
||||
margin: 0 auto;
|
||||
padding: 15% 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<div>
|
||||
<div id="main">
|
||||
<nz-alert [nzType]="type" [nzMessage]="message"
|
||||
[nzDescription]="desc" nzShowIcon>
|
||||
</nz-alert>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EmailVerifyComponent } from './email-verify.component';
|
||||
|
||||
describe('EmailVerifyComponent', () => {
|
||||
let component: EmailVerifyComponent;
|
||||
let fixture: ComponentFixture<EmailVerifyComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ EmailVerifyComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EmailVerifyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
58
index/src/app/pages/email-verify/email-verify.component.ts
Normal file
58
index/src/app/pages/email-verify/email-verify.component.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {Router, ActivatedRoute} from '@angular/router';
|
||||
import {UserService} from '../../services/user/user.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-email-verify',
|
||||
templateUrl: './email-verify.component.html',
|
||||
styleUrls: ['./email-verify.component.css']
|
||||
})
|
||||
export class EmailVerifyComponent implements OnInit {
|
||||
|
||||
constructor(private titleService: Title,
|
||||
public userService: UserService,
|
||||
private router: Router,
|
||||
public routerinfo: ActivatedRoute) {
|
||||
titleService.setTitle('小海博客|邮箱验证');
|
||||
}
|
||||
|
||||
type: string = 'info';
|
||||
message: string = '正在验证,请稍等';
|
||||
desc: string = '';
|
||||
|
||||
|
||||
private email: string;
|
||||
private verifyId: string;
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
this.email = this.routerinfo.snapshot.queryParams.email;
|
||||
this.verifyId = this.routerinfo.snapshot.queryParams.verifyId;
|
||||
if (this.email == null || this.verifyId == null) {
|
||||
this.type = 'warning';
|
||||
this.message = '数据不全';
|
||||
this.desc = '链接可能被修改了,请重新点击邮箱中的链接,或者重新发送邮件';
|
||||
}
|
||||
const reqBody = {
|
||||
email: this.email,
|
||||
verifyId: this.verifyId
|
||||
};
|
||||
this.userService.emailVerify(reqBody).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.type = 'success';
|
||||
this.message = '验证成功';
|
||||
// this.desc = "5秒后转跳到后台"
|
||||
// setTimeout(() => {
|
||||
// window.location.href = "admin"
|
||||
// }, 5000);
|
||||
} else {
|
||||
this.type = 'error';
|
||||
this.message = data.msg;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
180
index/src/app/pages/index/index.component.css
Normal file
180
index/src/app/pages/index/index.component.css
Normal file
@@ -0,0 +1,180 @@
|
||||
hr {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.main button {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
margin-right: 15px;
|
||||
margin-left: 15px;
|
||||
margin-top: 10px;
|
||||
background: none;
|
||||
border: 1px solid #000000;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.contact {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
/* info */
|
||||
.main {
|
||||
background-color: #ffffff;
|
||||
border-radius: 10px;
|
||||
width: 350px;
|
||||
margin-bottom: 50px;
|
||||
padding-bottom: 10px;
|
||||
word-break: break-all
|
||||
}
|
||||
|
||||
span {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.countInfo i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.countInfo li{
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
/* article */
|
||||
|
||||
.article {
|
||||
min-height: 200px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.aUl {
|
||||
margin: 20px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.aUl li {
|
||||
margin-right: 12px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.aUl li i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.article .aTitle {
|
||||
margin: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/** todo : 动画效果 */
|
||||
.aTitle:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.article .aType {
|
||||
color: #ffffff;
|
||||
font-size: 0.8em;
|
||||
padding: 2px 6px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.aTypeOriginal {
|
||||
background: #5eb95e;
|
||||
}
|
||||
|
||||
.aTypeReprint {
|
||||
background: #F37B1D;
|
||||
}
|
||||
|
||||
.aSummary {
|
||||
width: 98%;
|
||||
padding-top: 10px;
|
||||
font-size: 1.1em;
|
||||
line-height: 2em;
|
||||
color: #666666;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 自动换行 */
|
||||
.aTitle, .aSummary {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.tags {
|
||||
margin-top: 10px;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.about-contact-icon:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#pagination {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 768px) {
|
||||
.left {
|
||||
width: 380px;
|
||||
height: auto;
|
||||
flex-grow: 0;
|
||||
border-radius: 10px;
|
||||
margin-left: 80px;
|
||||
padding-bottom: 80px;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.right {
|
||||
height: auto;
|
||||
max-width: 1000px;
|
||||
flex-grow: 1;
|
||||
margin-left: 80px;
|
||||
margin-right: 80px;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
direction: rtl
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@media only screen and ( max-width: 768px ) {
|
||||
#bloger {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.article {
|
||||
margin: 0 10px 30px 10px;
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 94%;
|
||||
margin-left: 3%;
|
||||
}
|
||||
|
||||
.left {
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
115
index/src/app/pages/index/index.component.html
Normal file
115
index/src/app/pages/index/index.component.html
Normal file
@@ -0,0 +1,115 @@
|
||||
<div class="container">
|
||||
|
||||
|
||||
<h2 *ngIf="!articleService.currentPage" style="width: 100%;text-align: center">.暂时还未发布文章.</h2>
|
||||
|
||||
<div class="right" *ngIf="articleService.currentPage">
|
||||
<nz-card class="article am-animation-slide-right" *ngFor="let article of articleService.currentPage.list"
|
||||
[nzLoading]="!articleService.currentPage">
|
||||
|
||||
<a [routerLink]="'/article/'+article.id"><h2 class="aTitle">{{article.title}}</h2></a>
|
||||
<ul class="aUl">
|
||||
<li>
|
||||
<span class="aType" [ngClass]="{'aTypeOriginal': article.original,'aTypeReprint':!article.original}">
|
||||
{{article.original ? "原创" : "转载"}}
|
||||
</span>
|
||||
</li>
|
||||
<li><i nz-icon nzType="calendar" nzTheme="outline"></i>{{article.publishDateFormat}}</li>
|
||||
<li><i nz-icon nzType="user" nzTheme="outline"></i>{{article.authorName}}</li>
|
||||
<li>
|
||||
<i nz-icon nzType="folder" nzTheme="outline"></i>
|
||||
<a [routerLink]="['/category']" [queryParams]="{'name':article.category}">{{article.category}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="aSummary">
|
||||
<p>{{article.summary}}</p>
|
||||
</div>
|
||||
<a [routerLink]="'/article/'+article.id" style="font-size: 1.2em;">
|
||||
阅读全文
|
||||
<i nz-icon nzType="double-right" nzTheme="outline"></i>
|
||||
</a>
|
||||
<hr>
|
||||
<div class="tags">
|
||||
<span *ngFor="let item of article.tags">
|
||||
<i nz-icon nzType="tag" nzTheme="outline"></i>
|
||||
<a [routerLink]="['/tag']" [queryParams]="{name:item}">{{item}}</a>
|
||||
</span>
|
||||
</div>
|
||||
</nz-card>
|
||||
|
||||
<nz-pagination id="pagination" [nzPageIndex]="pageNum" [nzTotal]="articleService.currentPage.total"
|
||||
[nzPageSize]="pageSize"
|
||||
[nzHideOnSinglePage]="true" (nzPageIndexChange)="getPageArticle($event)"></nz-pagination>
|
||||
</div>
|
||||
|
||||
<div class="left">
|
||||
<!-- 关于我 -->
|
||||
<div class="main am-animation-slide-left" id="bloger">
|
||||
<span class="title">关于我</span>
|
||||
<hr>
|
||||
<div style="text-align: center;">
|
||||
<div class="am-center" align="center">
|
||||
<img [src]="imagePath" width="100px" height="100px" alt="logo">
|
||||
<h2>小海</h2>
|
||||
</div>
|
||||
<div class="about-desc">
|
||||
<span>一个爱好瞎捣鼓的技术宅 : )</span>
|
||||
</div>
|
||||
<div class="about-contact">
|
||||
<button class="about-contact-icon" (mouseenter)="showWxImg()" (mouseleave)="reset()">
|
||||
<i nz-icon nzType="wechat" nzTheme="outline"></i>
|
||||
</button>
|
||||
<button class="about-contact-icon" (mouseenter)="showQQImg()" (mouseleave)="reset()">
|
||||
<i nz-icon nzType="qq" nzTheme="outline"></i>
|
||||
</button>
|
||||
<a href="https://github.com/xiaohai2271">
|
||||
<button class="about-contact-icon">
|
||||
<i nz-icon nzType="github" nzTheme="outline"></i>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 标签云 -->
|
||||
<div class="main am-animation-slide-left">
|
||||
<span class="title">标签云</span>
|
||||
<hr>
|
||||
<div style="margin: 10px;">
|
||||
<span *ngIf="!tagService.tagCloudList">暂无标签</span>
|
||||
<div *ngIf="tagService.tagCloudList">
|
||||
<a *ngFor="let item of tagService.tagCloudList" style="max-width: 90%;word-break: break-all;margin: 0 4px;"
|
||||
[routerLink]="['/tag']" [queryParams]="{name:item.name}">
|
||||
<font [size]="item.size+1" [nzTitle]="item.name" nzPlacement="topCenter" nz-tooltip>{{item.name}}</font>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 网站信息 -->
|
||||
<div class="main am-animation-slide-left">
|
||||
<span class="title">网站信息</span>
|
||||
<hr>
|
||||
<div style="margin: 15px;" *ngIf="countService.count">
|
||||
<ul class="countInfo">
|
||||
<li><i nz-icon nzType="file" nzTheme="outline"></i><span>文章总数</span>:{{countService.count.articleCount}} 篇
|
||||
</li>
|
||||
<li><i nz-icon nzType="tags" nzTheme="outline"></i><span>标签总数</span>:{{countService.count.tagCount}} 个
|
||||
</li>
|
||||
<li><i nz-icon nzType="profile" nzTheme="outline"></i><span>留言总数</span>:{{countService.count.leaveMsgCount}} 条
|
||||
</li>
|
||||
<li><i nz-icon nzType="message" nzTheme="outline"></i><span>评论总数</span>:{{countService.count.commentCount}} 条
|
||||
</li>
|
||||
<li><i nz-icon nzType="eye" nzTheme="outline"></i><span>总访客量</span>:{{countService.count.visitorCount}} 条
|
||||
</li>
|
||||
<li><i nz-icon nzType="arrow-up" nzTheme="outline"></i><span>最后更新</span>:<span
|
||||
class="siteUpdateTime">{{webUpdateService.lastestUpdateTime}}</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div *ngIf="!countService.count" style="margin-left: 15px;">暂无更多信息</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
25
index/src/app/pages/index/index.component.spec.ts
Normal file
25
index/src/app/pages/index/index.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { IndexComponent } from './index.component';
|
||||
|
||||
describe('IndexComponent', () => {
|
||||
let component: IndexComponent;
|
||||
let fixture: ComponentFixture<IndexComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ IndexComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(IndexComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
76
index/src/app/pages/index/index.component.ts
Normal file
76
index/src/app/pages/index/index.component.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {Location} from '@angular/common';
|
||||
import {TagService} from '../../services/tag/tag.service';
|
||||
import {CountService} from '../../services/count/count.service';
|
||||
import {ArticleService} from '../../services/article/article.service';
|
||||
import {WebUpdateService} from '../../services/update/web-update.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-index',
|
||||
templateUrl: './index.component.html',
|
||||
styleUrls: ['./index.component.css']
|
||||
})
|
||||
export class IndexComponent implements OnInit {
|
||||
|
||||
constructor(public tagService: TagService,
|
||||
public countService: CountService,
|
||||
public articleService: ArticleService,
|
||||
public webUpdateService: WebUpdateService,
|
||||
private routerinfo: ActivatedRoute, private title: Title,
|
||||
private location: Location) {
|
||||
title.setTitle('小海博客');
|
||||
}
|
||||
|
||||
public pageNum: number;
|
||||
public pageSize: number;
|
||||
|
||||
imagePath: string;
|
||||
|
||||
ngOnInit() {
|
||||
this.pageNum = this.routerinfo.snapshot.queryParams.page;
|
||||
this.pageSize = this.routerinfo.snapshot.queryParams.count;
|
||||
// 数据合法性验证
|
||||
this.pageNum = (this.pageNum == null || this.pageNum < 1) ? 1 : this.pageNum;
|
||||
this.pageSize = (this.pageSize == null || this.pageSize < 1) ? 5 : this.pageSize;
|
||||
// 获取数据
|
||||
|
||||
if (this.tagService.tagCloudList == null) {
|
||||
this.tagService.getTagCloud();
|
||||
}
|
||||
if (this.countService.count == null) {
|
||||
this.countService.getCount();
|
||||
}
|
||||
this.articleService.getArticle(this.pageNum, this.pageSize);
|
||||
if (this.webUpdateService.updateInfoList == null) {
|
||||
this.webUpdateService.getUpdateInfo();
|
||||
}
|
||||
if (!this.webUpdateService.lastestUpdateTime) {
|
||||
this.webUpdateService.getLastestUpdateTime();
|
||||
}
|
||||
|
||||
// 设置imagePath的初始值
|
||||
this.reset();
|
||||
}
|
||||
|
||||
showQQImg() {
|
||||
this.imagePath = 'https://56462271.oss-cn-beijing.aliyuncs.com/web/qq.jpg';
|
||||
}
|
||||
|
||||
showWxImg() {
|
||||
this.imagePath = 'https://56462271.oss-cn-beijing.aliyuncs.com/web/wx.jpg';
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.imagePath = 'https://56462271.oss-cn-beijing.aliyuncs.com/web/logo.png';
|
||||
}
|
||||
|
||||
getPageArticle(e: number) {
|
||||
this.pageNum = e;
|
||||
// 修改地址栏
|
||||
this.location.replaceState('', '?page=' + e + '&count=' + this.pageSize);
|
||||
this.articleService.getArticle(this.pageNum, this.pageSize);
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
53
index/src/app/pages/leave-msg/leave-msg.component.css
Normal file
53
index/src/app/pages/leave-msg/leave-msg.component.css
Normal file
@@ -0,0 +1,53 @@
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.leaveMsg-input {
|
||||
width: 60%;
|
||||
margin-left: 20%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.leaveMsg-input textarea {
|
||||
padding: 10px;
|
||||
max-height: 400px;
|
||||
min-height: 60px;
|
||||
width: 500px;
|
||||
height: 100px;
|
||||
border: 1px solid #4398ED;
|
||||
border-radius: 10px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
#leaveMsg {
|
||||
width: 70%;
|
||||
height: 80%;
|
||||
margin-top: 45px;
|
||||
margin-left: 15%;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.comment {
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
#leaveMsgs {
|
||||
width: 60%;
|
||||
margin: 15px auto
|
||||
}
|
||||
|
||||
@media only screen and ( max-width:768px) {
|
||||
#leaveMsgs {
|
||||
width: 96%;
|
||||
margin-left: 2%;
|
||||
}
|
||||
#input-area {
|
||||
width: 80%;
|
||||
margin-left: 10%;
|
||||
}
|
||||
}
|
||||
82
index/src/app/pages/leave-msg/leave-msg.component.html
Normal file
82
index/src/app/pages/leave-msg/leave-msg.component.html
Normal file
@@ -0,0 +1,82 @@
|
||||
<div>
|
||||
|
||||
<span *ngIf="!userService.userInfo" style="display: block;text-align: center">
|
||||
若要评论,请先<a style="color: blue !important;" (click)="userService.showModal('login')">登录</a>哟~~~~
|
||||
</span>
|
||||
<nz-comment *ngIf="userService.userInfo" style="width: 60%;margin:15px auto">
|
||||
<nz-comment-content>
|
||||
<nz-form-item>
|
||||
<textarea [(ngModel)]="content" nz-input style="height: 130px;width: 80%;"></textarea>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<button nz-button nzType="primary" [disabled]="!content" (click)="submitComment()">
|
||||
评论
|
||||
</button>
|
||||
</nz-form-item>
|
||||
</nz-comment-content>
|
||||
</nz-comment>
|
||||
|
||||
|
||||
<!-- 展示 -->
|
||||
<nz-card id="leaveMsgs" [nzLoading]="!leaveMsgService.leaveMsgPage">
|
||||
<nz-comment *ngFor="let leaveMsg of leaveMsgService.leaveMsgPage.list; let i = index"
|
||||
[nzAuthor]="leaveMsg.authorName"
|
||||
[nzDatetime]="leaveMsg.date">
|
||||
<nz-avatar nz-comment-avatar nzIcon="user" [nzSrc]="leaveMsg.authorAvatarImgUrl"></nz-avatar>
|
||||
<nz-comment-content class="comment">
|
||||
<p>
|
||||
{{leaveMsg.content}}
|
||||
</p>
|
||||
</nz-comment-content>
|
||||
<nz-comment-action><span (click)="replyTo(leaveMsg.id,leaveMsg.authorName,i)">
|
||||
<i nz-icon nzType="message" nzTheme="fill"></i> 回复</span>
|
||||
</nz-comment-action>
|
||||
|
||||
<!-- 二级评论 -->
|
||||
<ng-container *ngIf="leaveMsg.child && leaveMsg.child.length">
|
||||
|
||||
<nz-comment *ngFor="let secLeaveMsg of leaveMsg.child" [nzAuthor]="secLeaveMsg.authorName"
|
||||
[nzDatetime]="secLeaveMsg.date">
|
||||
<nz-avatar nz-comment-avatar nzIcon="user" [nzSrc]="secLeaveMsg.authorAvatarImgUrl"></nz-avatar>
|
||||
<nz-comment-content class="comment">
|
||||
<p>
|
||||
{{secLeaveMsg.content}}
|
||||
</p>
|
||||
</nz-comment-content>
|
||||
<!-- <nz-comment-action><span (click)="replyTo(secLeaveMsg.id,secLeaveMsg.authorName,i)">
|
||||
<i nz-icon nzType="message" nzTheme="fill"></i> 回复</span>
|
||||
</nz-comment-action> -->
|
||||
|
||||
</nz-comment>
|
||||
|
||||
</ng-container>
|
||||
<!-- 二级评论的回复框 -->
|
||||
<nz-comment *ngIf="responseComment.pid!=null&&relyIndex==i">
|
||||
<nz-comment>
|
||||
<nz-comment-content>
|
||||
<nz-form-item>
|
||||
<textarea [(ngModel)]="responseComment.content" nz-input
|
||||
style="height: 130px;width: 80%;padding: 10px;border-radius: 5px;"></textarea>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<button nz-button nzType="primary" [disabled]="!responseComment.content"
|
||||
(click)="reply()"> 回复
|
||||
</button>
|
||||
<button nz-button nzType="default" style="margin-left: 15px;" (click)="responseComment.pid=null"> 取消
|
||||
</button>
|
||||
</nz-form-item>
|
||||
</nz-comment-content>
|
||||
</nz-comment>
|
||||
</nz-comment>
|
||||
</nz-comment>
|
||||
|
||||
|
||||
<nz-pagination style="text-align: center" [nzPageIndex]="pageNum" [nzTotal]="leaveMsgService.leaveMsgPage.total"
|
||||
[nzPageSize]="pageSize"
|
||||
[nzHideOnSinglePage]="true" (nzPageIndexChange)="toPage($event)"></nz-pagination>
|
||||
|
||||
|
||||
</nz-card>
|
||||
|
||||
|
||||
</div>
|
||||
25
index/src/app/pages/leave-msg/leave-msg.component.spec.ts
Normal file
25
index/src/app/pages/leave-msg/leave-msg.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LeaveMsgComponent } from './leave-msg.component';
|
||||
|
||||
describe('LeaveMsgComponent', () => {
|
||||
let component: LeaveMsgComponent;
|
||||
let fixture: ComponentFixture<LeaveMsgComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ LeaveMsgComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LeaveMsgComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
107
index/src/app/pages/leave-msg/leave-msg.component.ts
Normal file
107
index/src/app/pages/leave-msg/leave-msg.component.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {NzMessageService} from 'ng-zorro-antd';
|
||||
import {CommentService} from '../../services/comment/comment.service';
|
||||
import {UserService} from '../../services/user/user.service';
|
||||
import {CommentReq} from '../../class/commentReq';
|
||||
|
||||
@Component({
|
||||
selector: 'app-leave-msg',
|
||||
templateUrl: './leave-msg.component.html',
|
||||
styleUrls: ['./leave-msg.component.css']
|
||||
})
|
||||
export class LeaveMsgComponent implements OnInit {
|
||||
|
||||
constructor(public leaveMsgService: CommentService,
|
||||
private message: NzMessageService,
|
||||
public userService: UserService,
|
||||
private titleService: Title) {
|
||||
titleService.setTitle('小海博客|留言');
|
||||
// todo ::: @ {pid} 的name
|
||||
}
|
||||
|
||||
pageNum: number = 1;
|
||||
|
||||
pageSize: number = 10;
|
||||
|
||||
content: string = null;
|
||||
|
||||
// 当前的回复框的所处位置
|
||||
relyIndex: number;
|
||||
|
||||
public responseComment: CommentReq = new CommentReq(false);
|
||||
|
||||
ngOnInit() {
|
||||
this.getPageLeaveMsg();
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
getPageLeaveMsg() {
|
||||
this.leaveMsgService.getLeaveMsg(this.pageNum, this.pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击回复 显示输入框 及准备数据
|
||||
* @param id 父级评论的id
|
||||
* @param name 父级评论者的name
|
||||
* @param index 索引
|
||||
*/
|
||||
replyTo(id, name, index) {
|
||||
if (this.userService.userInfo == null) {
|
||||
this.message.info('请先登录哟~~~');
|
||||
return;
|
||||
}
|
||||
this.responseComment.pid = id;
|
||||
this.responseComment.content = ' @' + name + ' ';
|
||||
this.relyIndex = index;
|
||||
}
|
||||
|
||||
|
||||
toPage(a: number) {
|
||||
if (a === this.pageNum) {
|
||||
return;
|
||||
}
|
||||
this.pageNum = a;
|
||||
this.getPageLeaveMsg();
|
||||
}
|
||||
|
||||
/**
|
||||
* 留言
|
||||
*/
|
||||
submitComment() {
|
||||
if (this.content == null || this.content === '') {
|
||||
this.message.info('内容不能为空');
|
||||
return;
|
||||
}
|
||||
const submitBody: CommentReq = new CommentReq(false);
|
||||
submitBody.content = this.content;
|
||||
this.leaveMsgService.submitComment(submitBody);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 回复的数据提交
|
||||
*/
|
||||
reply() {
|
||||
if (this.responseComment.content == null || this.responseComment.content === '') {
|
||||
this.message.info('内容不能为空');
|
||||
return;
|
||||
}
|
||||
if (this.responseComment.pid == null) {
|
||||
this.message.error('非法操作');
|
||||
}
|
||||
this.responseComment.articleID = -1;
|
||||
this.leaveMsgService.rely(this.responseComment).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
if (this.leaveMsgService.leaveMsgPage.list[this.relyIndex].child == null) {
|
||||
this.leaveMsgService.leaveMsgPage.list[this.relyIndex].child = [];
|
||||
}
|
||||
this.leaveMsgService.leaveMsgPage.list[this.relyIndex].child.unshift(data.result);
|
||||
this.responseComment.content = null;
|
||||
this.responseComment.pid = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
51
index/src/app/pages/login/login.component.css
Normal file
51
index/src/app/pages/login/login.component.css
Normal file
@@ -0,0 +1,51 @@
|
||||
.main {
|
||||
}
|
||||
|
||||
input {
|
||||
margin-bottom: 20px;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.main-content {
|
||||
width: 500px;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%)
|
||||
}
|
||||
|
||||
#email, #password {
|
||||
width: 100%;
|
||||
border: 1px solid #999999;
|
||||
border-radius: 5px;
|
||||
height: 32px;
|
||||
background: #ffffff
|
||||
}
|
||||
|
||||
.br-text, .forget {
|
||||
clear: both;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border: none;
|
||||
padding: 4px 6px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.login {
|
||||
background: #0E90D2;
|
||||
color: white
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.main-content {
|
||||
width: 100%;
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
34
index/src/app/pages/login/login.component.html
Normal file
34
index/src/app/pages/login/login.component.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<div class="main">
|
||||
<div class="main-content">
|
||||
<form>
|
||||
<h1 style="text-align: center"><strong>小海</strong></h1>
|
||||
<label for="email">邮箱:</label>
|
||||
<br>
|
||||
<input type="email" name="email" id="email" value="" [(ngModel)]="submitBody.email">
|
||||
<br>
|
||||
<label for="password">密码:</label>
|
||||
<br>
|
||||
<input type="password" name="password" id="password" value="" [(ngModel)]="submitBody.password">
|
||||
<br>
|
||||
<label for="remember-me">
|
||||
<input id="remember-me" type="checkbox" name="isRemember" [(ngModel)]="submitBody.isRememberMe">
|
||||
记住密码
|
||||
</label>
|
||||
<div class="br-text">
|
||||
<p>
|
||||
<span>还没有账号?</span>
|
||||
<a style="color: blue" (click)="userService.showModal('registration')">注册</a>
|
||||
</p>
|
||||
</div>
|
||||
<br/>
|
||||
<div>
|
||||
<button type="submit" (click)="doLogin()" class="btn login">登 录</button>
|
||||
<button type="button" class="btn forget" (click)="showForgetPwd=!showForgetPwd">忘记密码 ^_^?</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nz-modal [(nzVisible)]="showForgetPwd" nzTitle="重置密码" (nzOnCancel)="handleCancel()" (nzOnOk)="handleOk()">
|
||||
<input type="email" nz-input placeholder="注册的邮箱" [(ngModel)]="email4Forgot"/>
|
||||
</nz-modal>
|
||||
25
index/src/app/pages/login/login.component.spec.ts
Normal file
25
index/src/app/pages/login/login.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginComponent } from './login.component';
|
||||
|
||||
describe('LoginComponent', () => {
|
||||
let component: LoginComponent;
|
||||
let fixture: ComponentFixture<LoginComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ LoginComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
94
index/src/app/pages/login/login.component.ts
Normal file
94
index/src/app/pages/login/login.component.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import {Component, OnInit, ViewChild} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {Router} from '@angular/router';
|
||||
import {UserService} from '../../services/user/user.service';
|
||||
import {NzMessageService} from 'ng-zorro-antd';
|
||||
import {LoginReq} from '../../class/loginReq';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.css']
|
||||
})
|
||||
export class LoginComponent implements OnInit {
|
||||
|
||||
constructor(public userService: UserService,
|
||||
private routerinfo: ActivatedRoute,
|
||||
private router: Router,
|
||||
private message: NzMessageService) {
|
||||
}
|
||||
|
||||
public submitBody: LoginReq = new LoginReq();
|
||||
|
||||
|
||||
showForgetPwd: boolean = false;
|
||||
|
||||
email4Forgot: string;
|
||||
|
||||
ngOnInit() {
|
||||
window.scrollTo(0, 0);
|
||||
if (this.userService.tempUser) {
|
||||
this.submitBody.email = this.userService.tempUser.email;
|
||||
this.submitBody.password = this.userService.tempUser.password;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 登录
|
||||
doLogin() {
|
||||
if (this.submitBody.email == null || this.submitBody.email === '') {
|
||||
this.message.warning('邮箱不能为空');
|
||||
return;
|
||||
}
|
||||
if (this.submitBody.password == null || this.submitBody.password === '') {
|
||||
this.message.warning('密码不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
const regExp = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
|
||||
if (!regExp.test(this.submitBody.email)) {
|
||||
this.message.error('邮箱格式不合法');
|
||||
return;
|
||||
}
|
||||
|
||||
this.userService.login(this.submitBody).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
// 登录成功
|
||||
// 置空
|
||||
this.userService.tempUser = null;
|
||||
this.message.success('登录成功,欢迎你' + data.result.displayName);
|
||||
} else {
|
||||
// 登录失败
|
||||
this.message.error('登录失败,原因:' + data.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
handleCancel() {
|
||||
this.showForgetPwd = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发生重置密码的邮件
|
||||
*/
|
||||
handleOk() {
|
||||
if (this.email4Forgot == null) {
|
||||
return;
|
||||
}
|
||||
const regExp = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
|
||||
if (!regExp.test(this.email4Forgot)) {
|
||||
this.message.error('邮箱格式不合法');
|
||||
return;
|
||||
}
|
||||
this.showForgetPwd = false;
|
||||
this.userService.sendResetPwdEmail(this.email4Forgot).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.message.success('发送成功,请前往邮箱进行操作');
|
||||
} else {
|
||||
this.message.error(data.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
87
index/src/app/pages/not-found/not-found.component.css
Normal file
87
index/src/app/pages/not-found/not-found.component.css
Normal file
@@ -0,0 +1,87 @@
|
||||
div,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
img,
|
||||
strong,
|
||||
b,
|
||||
i,
|
||||
dl,
|
||||
dt,
|
||||
dd,
|
||||
ol,
|
||||
ul,
|
||||
li,
|
||||
fieldset,
|
||||
form,
|
||||
label,
|
||||
legend,
|
||||
table,
|
||||
caption,
|
||||
tbody,
|
||||
tfoot,
|
||||
thead,
|
||||
tr,
|
||||
th,
|
||||
td,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
button,
|
||||
input,
|
||||
textarea {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
vertical-align: baseline;
|
||||
background: transparent;
|
||||
font-family: 'Microsoft Yahei', '\65B0\5B8B\4F53', '\5B8B\4F53', Verdana
|
||||
}
|
||||
|
||||
.error404 {
|
||||
width: 1100px;
|
||||
height: 460px;
|
||||
background: url(../../../assets/img/404.jpg) 50px;
|
||||
background-size: 100% 100%;
|
||||
margin: 3px auto 0
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00c0ff !important;
|
||||
}
|
||||
|
||||
.wenzi {
|
||||
padding-top: 326px;
|
||||
text-align: center;
|
||||
font-family: "微软雅黑";
|
||||
font-size: 26px;
|
||||
color: #686e6e
|
||||
}
|
||||
|
||||
.wenzi {
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
color: #888888;
|
||||
display: block;
|
||||
border-bottom: 1px solid #d0cfcf
|
||||
}
|
||||
|
||||
.wenzi_2 span {
|
||||
color: #319898;
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
font-style: italic
|
||||
}
|
||||
|
||||
@media only screen and (max-width:768px) {
|
||||
.error404 {
|
||||
width: 100%;
|
||||
background: url(../../../assets/img/404-m.jpg) -10px;
|
||||
}
|
||||
}
|
||||
6
index/src/app/pages/not-found/not-found.component.html
Normal file
6
index/src/app/pages/not-found/not-found.component.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<div class="error404">
|
||||
<div class="wenzi">
|
||||
<div class="wenzi-1">亲,您已经飘到外太空了哦!</div>
|
||||
<div class="wenzi_2">想回地球请点击:<a href="/">小海博客</a></div>
|
||||
</div>
|
||||
</div>
|
||||
25
index/src/app/pages/not-found/not-found.component.spec.ts
Normal file
25
index/src/app/pages/not-found/not-found.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NotFoundComponent } from './not-found.component';
|
||||
|
||||
describe('NotFoundComponent', () => {
|
||||
let component: NotFoundComponent;
|
||||
let fixture: ComponentFixture<NotFoundComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ NotFoundComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NotFoundComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
28
index/src/app/pages/not-found/not-found.component.ts
Normal file
28
index/src/app/pages/not-found/not-found.component.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Router, NavigationError} from '@angular/router';
|
||||
import {Location} from '@angular/common';
|
||||
|
||||
import {filter} from 'rxjs/operators';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
|
||||
@Component({
|
||||
selector: 'app-not-found',
|
||||
templateUrl: './not-found.component.html',
|
||||
styleUrls: ['./not-found.component.css']
|
||||
})
|
||||
export class NotFoundComponent implements OnInit {
|
||||
|
||||
constructor(router: Router, location: Location, private titleService: Title) {
|
||||
titleService.setTitle('小海博客|404');
|
||||
router.events.pipe(
|
||||
filter(event => event instanceof NavigationError)
|
||||
).subscribe((event: NavigationError) => {
|
||||
router.navigate(['/404'], {skipLocationChange: true})
|
||||
.then(() => location.go(event.url));
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
101
index/src/app/pages/partner-sites/partner-sites.component.css
Normal file
101
index/src/app/pages/partner-sites/partner-sites.component.css
Normal file
@@ -0,0 +1,101 @@
|
||||
|
||||
i {
|
||||
margin-right: 10px;
|
||||
color: royalblue;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #909090
|
||||
}
|
||||
|
||||
.site-middle {
|
||||
width: 60%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.partner-sites {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 20px 10px;
|
||||
}
|
||||
|
||||
.partner-sites li {
|
||||
width: 25%;
|
||||
margin: 10px 0;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.placard {
|
||||
width: 60%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.placard-content {
|
||||
margin-top: 30px;
|
||||
margin-left: 30px;
|
||||
border-left: 5px solid #aaa4a4;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.placard-content li {
|
||||
padding-left: 15px;
|
||||
font-size: 1.2em;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.applylink {
|
||||
float: right;
|
||||
border: none;
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.applylink:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.contentinput {
|
||||
margin-left: 8px;
|
||||
width: 60%;
|
||||
height: 32px;
|
||||
background: #fafafa;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border: none;
|
||||
padding: 5px 8px;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cancel {
|
||||
background: #aaa4a4
|
||||
}
|
||||
|
||||
.submit {
|
||||
background: #2799FF
|
||||
}
|
||||
|
||||
@media only screen and ( max-width: 768px ) {
|
||||
.site-middle, .placard {
|
||||
margin-left: 2px;
|
||||
width: 96%;
|
||||
}
|
||||
|
||||
.partner-sites li {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<div class="site-middle am-animation-slide-top">
|
||||
<div class="title">
|
||||
<i nz-icon nzType="smile" nzTheme="outline" class="titleTag"></i><span class="title">友情链接</span>
|
||||
|
||||
</div>
|
||||
<ul class="partner-sites">
|
||||
<li *ngFor="let link of linkService.Links">
|
||||
<i nz-icon nzType="link" nzTheme="outline"></i>
|
||||
<a [href]="link.url" target="_blank" [title]="link.name">{{link.name}}</a>
|
||||
</li>
|
||||
<li class="applylink" (click)="showModal=!showModal">申请友链</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="placard am-animation-slide-bottom">
|
||||
<div class="title">
|
||||
<i nz-icon nzType="smile" nzTheme="outline" class="titleTag"></i><span class="title">友链公告</span>
|
||||
</div>
|
||||
<ul class="placard-content">
|
||||
<li>请确认贵站可正常访问</li>
|
||||
<li>原创博客、技术博客、游记博客优先</li>
|
||||
<li>博客内容时常更新</li>
|
||||
<li><strong>提交申请时若为https链接请带上https否则将视为http</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<nz-modal [(nzVisible)]="showModal" [nzTitle]="modalTitle" [nzContent]="modalContent" [nzFooter]="modalFooter"
|
||||
(nzOnCancel)="cancel()">
|
||||
<ng-template #modalTitle>
|
||||
<h2 style="text-align: center">申请友链</h2>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #modalContent>
|
||||
<div class="am-modal-bd">
|
||||
<div class="article-setting">
|
||||
<label>网站名称:</label>
|
||||
<input class="contentinput" placeholder="请输入网站名称" [(ngModel)]="link.name">
|
||||
<br>
|
||||
<label>网站链接:</label>
|
||||
<input class="contentinput" placeholder="请输入网站链接" [(ngModel)]="link.url">
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #modalFooter>
|
||||
<button class="btn cancel" (click)="cancel()">取消</button>
|
||||
<button class="btn submit" (click)="apply()">提交</button>
|
||||
</ng-template>
|
||||
</nz-modal>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PartnerSitesComponent } from './partner-sites.component';
|
||||
|
||||
describe('PartnerSitesComponent', () => {
|
||||
let component: PartnerSitesComponent;
|
||||
let fixture: ComponentFixture<PartnerSitesComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ PartnerSitesComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PartnerSitesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
65
index/src/app/pages/partner-sites/partner-sites.component.ts
Normal file
65
index/src/app/pages/partner-sites/partner-sites.component.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {NzMessageService} from 'ng-zorro-antd';
|
||||
import {LinkService} from '../../services/link/link.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-partner-sites',
|
||||
templateUrl: './partner-sites.component.html',
|
||||
styleUrls: ['./partner-sites.component.css']
|
||||
})
|
||||
export class PartnerSitesComponent implements OnInit {
|
||||
|
||||
constructor(public linkService: LinkService,
|
||||
private message: NzMessageService,
|
||||
private titleService: Title) {
|
||||
titleService.setTitle('小海博客|友链');
|
||||
}
|
||||
|
||||
showModal: boolean = false;
|
||||
|
||||
// 申请时的链接
|
||||
link = {
|
||||
name: '',
|
||||
url: ''
|
||||
};
|
||||
|
||||
ngOnInit() {
|
||||
window.scrollTo(0, 0);
|
||||
if (this.linkService.Links == null) {
|
||||
this.linkService.getLinks();
|
||||
}
|
||||
}
|
||||
|
||||
apply() {
|
||||
if (this.link.name === '') {
|
||||
this.message.error('网站名称不能为空');
|
||||
return;
|
||||
}
|
||||
if (this.link.url === '') {
|
||||
this.message.error('网站链接不能为空');
|
||||
return;
|
||||
}
|
||||
const regExp = /^(https:\/\/|http:\/\/|)([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?$/;
|
||||
if (!regExp.test(this.link.url)) {
|
||||
this.message.error('网站链接输入不合法');
|
||||
return;
|
||||
}
|
||||
this.showModal = false;
|
||||
this.linkService.apply(this.link).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.message.success('提交成功,请稍等,即将为你处理');
|
||||
} else {
|
||||
this.message.error('提交失败,原因:' + data.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.showModal = false;
|
||||
this.link.name = null;
|
||||
this.link.url = null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
93
index/src/app/pages/registration/registration.component.css
Normal file
93
index/src/app/pages/registration/registration.component.css
Normal file
@@ -0,0 +1,93 @@
|
||||
.main {
|
||||
width: 500px;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.ioc_text {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.remain {
|
||||
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin: 20px 0 0 12%;
|
||||
height: 32px;
|
||||
|
||||
}
|
||||
|
||||
input {
|
||||
width: 75%;
|
||||
height: 32px;
|
||||
background: #fafafa;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.verStatus {
|
||||
width: 10px;
|
||||
line-height: 32px;
|
||||
margin-left: -20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.br-text {
|
||||
margin-left: 12%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 80%;
|
||||
height: 40px;
|
||||
margin-left: 10%;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
background: turquoise;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3385FF
|
||||
}
|
||||
|
||||
|
||||
/* 全屏遮罩 */
|
||||
.fullS {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.loading {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
background: none;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.loading span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.main {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
56
index/src/app/pages/registration/registration.component.html
Normal file
56
index/src/app/pages/registration/registration.component.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<div>
|
||||
<div class="main">
|
||||
<div class="ioc_text">
|
||||
<h1><strong>小海</strong></h1>
|
||||
</div>
|
||||
<form>
|
||||
<div class="remain">
|
||||
<div class="input-group mb-4 bootint" id="username">
|
||||
<!-- <div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-user"></i></span>
|
||||
</div> -->
|
||||
<input type="text" name="email" class="form-control" placeholder="请输入你的邮箱" [(ngModel)]="email">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-4 bootint" id="password">
|
||||
<!-- <div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-unlock-alt"></i></span>
|
||||
</div> -->
|
||||
<input type="password" name="password" class="form-control" placeholder="请输入你要设置的密码(8位以上)"
|
||||
[(ngModel)]="password">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-4 bootint" id="repassword">
|
||||
<input type="password" name="repaassword" class="form-control" placeholder="请再次输入密码" [(ngModel)]="rePassword">
|
||||
<i nz-icon class="verStatus" [hidden]="rePassword==null" [nzType]="password==rePassword?'check-circle':'close-circle'" nzTheme="outline"></i>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" name="code" placeholder="验证码" (blur)="handleKeyUp()" [(ngModel)]="imgCode"
|
||||
class="form-control" style="width: 60%;" id="verifyImgCode">
|
||||
<i nz-icon class="verStatus" [hidden]="imgCode==''" [nzType]="imgCodeStatus?'check-circle':'close-circle'" nzTheme="outline"></i>
|
||||
<img [src]="imgCodeUrl" (click)="changeImg()" alt="Code" title="点击更换验证码"/>
|
||||
</div>
|
||||
|
||||
<div class="br-text">
|
||||
<p>
|
||||
<span>已有账号了?</span>
|
||||
<a (click)="userService.showModal('login')">登录</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="padding-top: 10px">
|
||||
<button type="submit" class="btn" (click)="doRegistration()">注册</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fullS" *ngIf="show">
|
||||
<nz-spin [nzSpinning]="show" class="loading">
|
||||
<div class="loading">
|
||||
<span>加载中..</span>
|
||||
</div>
|
||||
</nz-spin>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RegistrationComponent } from './registration.component';
|
||||
|
||||
describe('RegistrationComponent', () => {
|
||||
let component: RegistrationComponent;
|
||||
let fixture: ComponentFixture<RegistrationComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ RegistrationComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RegistrationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
92
index/src/app/pages/registration/registration.component.ts
Normal file
92
index/src/app/pages/registration/registration.component.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {NzMessageService} from 'ng-zorro-antd';
|
||||
import {UserService} from '../../services/user/user.service';
|
||||
import {environment} from '../../../environments/environment';
|
||||
import {LoginReq} from '../../class/loginReq';
|
||||
|
||||
@Component({
|
||||
selector: 'app-registration',
|
||||
templateUrl: './registration.component.html',
|
||||
styleUrls: ['./registration.component.css']
|
||||
})
|
||||
export class RegistrationComponent implements OnInit {
|
||||
|
||||
|
||||
imgCodeUrl: string = environment.host + '/imgCode';
|
||||
|
||||
// 遮罩
|
||||
show = false;
|
||||
// 输入框的验证码
|
||||
imgCode: string = '';
|
||||
email: string = null;
|
||||
password: string = null;
|
||||
rePassword: string = null;
|
||||
// 验证码验证状态
|
||||
public imgCodeStatus: boolean;
|
||||
|
||||
constructor(public userService: UserService,
|
||||
private router: Router,
|
||||
private message: NzMessageService) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
changeImg() {
|
||||
this.imgCodeUrl = environment.host + '/imgCode?time=' + (new Date()).getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码验证
|
||||
*/
|
||||
handleKeyUp() {
|
||||
if (this.imgCode.length === 4) {
|
||||
this.userService.imgCodeVerify(this.imgCode).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.imgCodeStatus = true;
|
||||
} else {
|
||||
this.imgCodeStatus = false;
|
||||
this.message.warning('验证码验证失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册的数据提交
|
||||
*/
|
||||
doRegistration() {
|
||||
if (this.imgCodeStatus) {
|
||||
if (this.email == null || this.password == null || this.rePassword == null) {
|
||||
this.message.warning('用户名和密码均不能为空');
|
||||
return;
|
||||
}
|
||||
if (this.rePassword !== this.password) {
|
||||
this.message.warning('两次密码不匹配');
|
||||
return;
|
||||
}
|
||||
this.show = true;
|
||||
|
||||
this.userService.registration(this.email, this.password).subscribe(data => {
|
||||
this.show = false;
|
||||
this.userService.tempUser = new LoginReq();
|
||||
if (data.code === 0) {
|
||||
this.userService.tempUser.email = this.email;
|
||||
this.userService.tempUser.password = this.password;
|
||||
// 注册成功
|
||||
this.message.success('注册成功!');
|
||||
setTimeout(() => {
|
||||
// 换成登录的modal
|
||||
this.userService.loginModalType = 'login';
|
||||
}, 300);
|
||||
} else {
|
||||
this.message.error('注册失败,原因:' + data.msg);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
document.getElementById('verifyImgCode').focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
27
index/src/app/pages/reset-pwd/reset-pwd.component.css
Normal file
27
index/src/app/pages/reset-pwd/reset-pwd.component.css
Normal file
@@ -0,0 +1,27 @@
|
||||
#main{
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
width: 410px;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
padding: 5px;
|
||||
}
|
||||
button{
|
||||
display: block;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
background: #9FFE8E;
|
||||
height: 40px;
|
||||
width: 400px;
|
||||
}
|
||||
input{
|
||||
border: none;
|
||||
text-align: center;
|
||||
width: 400px;
|
||||
border-radius: 5px;
|
||||
height: 40px;
|
||||
margin-bottom: 10px;
|
||||
background: #eeeeee
|
||||
}
|
||||
13
index/src/app/pages/reset-pwd/reset-pwd.component.html
Normal file
13
index/src/app/pages/reset-pwd/reset-pwd.component.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<div style="height: 70%">
|
||||
<div id="main">
|
||||
<div *ngIf="iserror">
|
||||
<nz-alert nzType="error" nzMessage="链接可能被修改了,请重新点击邮箱中的链接,或者重新发送邮件" nzShowIcon>
|
||||
</nz-alert>
|
||||
</div>
|
||||
<div *ngIf="!iserror">
|
||||
<input type="password" placeholder="新密码" [(ngModel)]="pwd" />
|
||||
<input type="password" placeholder="确认密码" [(ngModel)]="rePwd" />
|
||||
<button (click)="submit()">提交</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
25
index/src/app/pages/reset-pwd/reset-pwd.component.spec.ts
Normal file
25
index/src/app/pages/reset-pwd/reset-pwd.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ResetPwdComponent } from './reset-pwd.component';
|
||||
|
||||
describe('ResetPwdComponent', () => {
|
||||
let component: ResetPwdComponent;
|
||||
let fixture: ComponentFixture<ResetPwdComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ResetPwdComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ResetPwdComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
71
index/src/app/pages/reset-pwd/reset-pwd.component.ts
Normal file
71
index/src/app/pages/reset-pwd/reset-pwd.component.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {NzMessageService} from 'ng-zorro-antd';
|
||||
import {Router, ActivatedRoute} from '@angular/router';
|
||||
import {UserService} from '../../services/user/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-reset-pwd',
|
||||
templateUrl: './reset-pwd.component.html',
|
||||
styleUrls: ['./reset-pwd.component.css']
|
||||
})
|
||||
export class ResetPwdComponent implements OnInit {
|
||||
|
||||
constructor(private message: NzMessageService,
|
||||
public userService: UserService,
|
||||
private router: Router,
|
||||
private routerinfo: ActivatedRoute) {
|
||||
}
|
||||
|
||||
private pwd: string;
|
||||
private rePwd: string;
|
||||
|
||||
private email: string;
|
||||
private verifyId: string;
|
||||
|
||||
iserror: boolean = false;
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
this.email = this.routerinfo.snapshot.queryParams.email;
|
||||
this.verifyId = this.routerinfo.snapshot.queryParams.verifyId;
|
||||
if (this.email == null || this.verifyId == null) {
|
||||
this.iserror = true;
|
||||
}
|
||||
}
|
||||
|
||||
submit() {
|
||||
if (this.pwd == null) {
|
||||
return;
|
||||
}
|
||||
if (this.pwd.length > 16) {
|
||||
this.message.error('密码过长');
|
||||
return;
|
||||
}
|
||||
if (this.pwd.length < 6) {
|
||||
this.message.error('密码过短');
|
||||
return;
|
||||
}
|
||||
if (this.pwd !== this.rePwd) {
|
||||
this.message.warning('两次密码不一致');
|
||||
}
|
||||
|
||||
const reqBody = {
|
||||
email: this.email,
|
||||
verifyId: this.verifyId,
|
||||
pwd: this.pwd
|
||||
};
|
||||
this.userService.resetPWd(reqBody).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.message.success('重置密码成功,5秒后转跳到登录页面。');
|
||||
setTimeout(() => {
|
||||
this.router.navigateByUrl('/login');
|
||||
// window.location.href = '/login';
|
||||
}, 5000);
|
||||
} else {
|
||||
this.message.error(data.msg);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
65
index/src/app/pages/tag/tag.component.css
Normal file
65
index/src/app/pages/tag/tag.component.css
Normal file
@@ -0,0 +1,65 @@
|
||||
#main{
|
||||
width:60%;
|
||||
margin:0 auto;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
background: #ffffff;
|
||||
}
|
||||
#tag{
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ececec;
|
||||
box-shadow: 5px 5px 5px #888888;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#detail{
|
||||
clear: both;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.singleTag{
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
line-height: 15px;
|
||||
margin: 5px 10px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
border: 1px solid #000000;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
ul{
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.art{
|
||||
padding: 8px;
|
||||
border: 1px solid #ececec;
|
||||
box-shadow: 5px 5px 5px #aaaaaa;
|
||||
margin: 20px 0 ;
|
||||
}
|
||||
.title:hover{
|
||||
animation: 1s move ;
|
||||
}
|
||||
@keyframes move{
|
||||
from{
|
||||
margin-left: 0;
|
||||
}
|
||||
to{
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.active{
|
||||
background: #6DAB90;
|
||||
}
|
||||
|
||||
@media only screen and (max-width:768px){
|
||||
|
||||
#main{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
25
index/src/app/pages/tag/tag.component.html
Normal file
25
index/src/app/pages/tag/tag.component.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<div id="main">
|
||||
|
||||
<div id="tag">
|
||||
<ul>
|
||||
<li *ngFor="let tag of tagService.tagCloudList" (click)="changeTag(tag.name)">
|
||||
<span class="singleTag" [ngClass]="{active: currentTagName==tag.name}" [nzTitle]="'此标签有'+tag.size+'篇文章'"
|
||||
nzPlacement="topCenter" nz-tooltip>
|
||||
<i nz-icon nzType="tag" nzTheme="fill"></i> {{tag.name}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<ul id="detail" *ngIf="curremtArticlePage">
|
||||
<li *ngFor="let article of curremtArticlePage.list">
|
||||
<div class="art">
|
||||
<h2><a [routerLink]="'/article/'+article.id" class="title">{{article.title}}</a></h2>
|
||||
<hr>
|
||||
<div> {{article.summary}}</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div *ngIf="!curremtArticlePage">
|
||||
<h2 style="text-align: center">该分类暂无文章</h2>
|
||||
</div>
|
||||
</div>
|
||||
25
index/src/app/pages/tag/tag.component.spec.ts
Normal file
25
index/src/app/pages/tag/tag.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {TagComponent} from './tag.component';
|
||||
|
||||
describe('TagComponent', () => {
|
||||
let component: TagComponent;
|
||||
let fixture: ComponentFixture<TagComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [TagComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TagComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
62
index/src/app/pages/tag/tag.component.ts
Normal file
62
index/src/app/pages/tag/tag.component.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {Router, ActivatedRoute} from '@angular/router';
|
||||
import {Location} from '@angular/common';
|
||||
import {TagService} from '../../services/tag/tag.service';
|
||||
import {ArticleService} from '../../services/article/article.service';
|
||||
import {Page} from '../../class/page';
|
||||
import {Article} from '../../class/article';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tags',
|
||||
templateUrl: './tag.component.html',
|
||||
styleUrls: ['./tag.component.css']
|
||||
})
|
||||
|
||||
export class TagComponent implements OnInit {
|
||||
|
||||
constructor(private titleService: Title,
|
||||
public tagService: TagService,
|
||||
public articleService: ArticleService,
|
||||
private router: Router,
|
||||
private routerinfo: ActivatedRoute,
|
||||
private location: Location) {
|
||||
titleService.setTitle('小海博客|标签');
|
||||
}
|
||||
|
||||
public currentTagName: string;
|
||||
public curremtArticlePage: Page<Article>;
|
||||
|
||||
ngOnInit() {
|
||||
window.scrollTo(0, 0);
|
||||
this.currentTagName = this.routerinfo.snapshot.queryParams.name;
|
||||
|
||||
if (this.tagService.tagCloudList == null) {
|
||||
this.tagService.getTagCloud().subscribe((data: any) => {
|
||||
if (data.code === 0) {
|
||||
// 根据当前获取到的tag name获取数据
|
||||
if (this.currentTagName == null) {
|
||||
this.currentTagName = data.result[0].name;
|
||||
}
|
||||
this.getArticle();
|
||||
}
|
||||
});
|
||||
} else if (this.currentTagName == null) {
|
||||
this.currentTagName = this.tagService.tagCloudList[0].name;
|
||||
}
|
||||
this.getArticle();
|
||||
}
|
||||
|
||||
|
||||
changeTag(name) {
|
||||
this.currentTagName = name;
|
||||
this.location.replaceState('tag', '?name=' + name);
|
||||
this.getArticle();
|
||||
}
|
||||
|
||||
getArticle() {
|
||||
this.articleService.getArticleByTag(this.currentTagName).subscribe(data => {
|
||||
this.curremtArticlePage = data.result;
|
||||
});
|
||||
}
|
||||
}
|
||||
96
index/src/app/pages/update/update.component.css
Normal file
96
index/src/app/pages/update/update.component.css
Normal file
@@ -0,0 +1,96 @@
|
||||
@media screen and (min-width: 768px) {
|
||||
.site-inner{
|
||||
margin: 0 19%;
|
||||
}
|
||||
.word p{
|
||||
text-indent: 7em;
|
||||
}
|
||||
.word{
|
||||
padding: 20px 25px;
|
||||
}
|
||||
.zh-update{
|
||||
margin: 0 25%;
|
||||
}
|
||||
.update-leave-message{
|
||||
margin: 100px 10%;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
.site-inner{
|
||||
margin: 0 5%;
|
||||
}
|
||||
.word p{
|
||||
text-indent: 2em;
|
||||
}
|
||||
.word{
|
||||
padding: 20px 5px;
|
||||
}
|
||||
.zh-update{
|
||||
margin: 0 3%;
|
||||
}
|
||||
}
|
||||
.am-g{
|
||||
margin: 50px 0 0;
|
||||
}
|
||||
.site-inner-img{
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
}
|
||||
.site-inner-img img{
|
||||
width: 450px;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
.site-inner-mywords{
|
||||
margin: 30px auto;
|
||||
}
|
||||
.word{
|
||||
margin-top: 10px;
|
||||
margin-left: 25%;
|
||||
font-size: 21px;
|
||||
line-height: 20px;
|
||||
color: blue;
|
||||
font-family: 华文仿宋;
|
||||
}
|
||||
.zh-update-log-title{
|
||||
margin: 50px 0;
|
||||
}
|
||||
.zh-update-log-title h1{
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
text-indent: 1em;
|
||||
}
|
||||
.zh-update-log-title p{
|
||||
text-indent: 1.5em;
|
||||
}
|
||||
.zh-update-log-content{
|
||||
margin-top: 10px;
|
||||
}
|
||||
.update-log i{
|
||||
color: #999999;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.update-log h2{
|
||||
display: inline-block;
|
||||
font-size: 1.3em;
|
||||
font-weight: 650;
|
||||
color: #333;
|
||||
}
|
||||
.update-log-content{
|
||||
margin: 15px 26px;
|
||||
}
|
||||
.update-log-content blockquote{
|
||||
padding: 0 10px 0 20px;
|
||||
border-left: 4px solid #ddd;
|
||||
}
|
||||
.update-log-content p{
|
||||
font-size: 16px;
|
||||
word-wrap: break-word;
|
||||
font-family: 微软雅黑;
|
||||
}
|
||||
|
||||
.container{
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
56
index/src/app/pages/update/update.component.html
Normal file
56
index/src/app/pages/update/update.component.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<div id="app">
|
||||
<!--页面主体-->
|
||||
<div id="main">
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="site-inner">
|
||||
|
||||
<div class="site-inner-img">
|
||||
|
||||
<img src="https://zhy-myblog.oss-cn-shenzhen.aliyuncs.com/static/img/sun.jpg" class="am-img-thumbnail">
|
||||
</div>
|
||||
|
||||
<div class="site-inner-mywords">
|
||||
<br>
|
||||
<div class="word">
|
||||
过自己喜欢的生活,<br><br>
|
||||
<p>做自己喜欢的人</p><br>
|
||||
<p>不以物喜</p>
|
||||
<p>不以己悲</p>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="zh-update">
|
||||
<div class="zh-update-log">
|
||||
<div class="zh-update-log-title">
|
||||
<h1>更新日志</h1>
|
||||
<p>最后更新于 {{updateService.lastestUpdateTime}}</p>
|
||||
</div>
|
||||
<div class="zh-update-log-content">
|
||||
|
||||
|
||||
<div class="update-log am-animation-slide-bottom" *ngFor="let update of updateService.updateInfoList">
|
||||
<div class="update-log-date">
|
||||
<i nz-icon nzType="star" nzTheme="twotone" nzTwotoneColor="#eb2f96" class="titleTag"></i>
|
||||
<h2>{{update.time}}</h2>
|
||||
</div>
|
||||
<div class="update-log-content">
|
||||
<blockquote>
|
||||
<p *ngFor="let item of update.info.split('\n')">{{item}}</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
25
index/src/app/pages/update/update.component.spec.ts
Normal file
25
index/src/app/pages/update/update.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UpdateComponent } from './update.component';
|
||||
|
||||
describe('UpdateComponent', () => {
|
||||
let component: UpdateComponent;
|
||||
let fixture: ComponentFixture<UpdateComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ UpdateComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(UpdateComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
28
index/src/app/pages/update/update.component.ts
Normal file
28
index/src/app/pages/update/update.component.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {WebUpdateService} from '../../services/update/web-update.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-update',
|
||||
templateUrl: './update.component.html',
|
||||
styleUrls: ['./update.component.css']
|
||||
})
|
||||
export class UpdateComponent implements OnInit {
|
||||
|
||||
constructor(public updateService: WebUpdateService,
|
||||
private titleService: Title) {
|
||||
titleService.setTitle('小海博客|更新');
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
if (this.updateService.updateInfoList == null) {
|
||||
this.updateService.getUpdateInfo();
|
||||
}
|
||||
window.scrollTo(0, 0);
|
||||
if (!this.updateService.lastestUpdateTime) {
|
||||
this.updateService.getLastestUpdateTime();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
30
index/src/app/pages/write/editor/editor-md.directive.ts
Normal file
30
index/src/app/pages/write/editor/editor-md.directive.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {AfterViewInit, Attribute, Directive, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {EditorConfig} from '../../../class/editor-config';
|
||||
|
||||
declare var editormd: any;
|
||||
declare var $: any;
|
||||
|
||||
@Directive({
|
||||
selector: '[appEditorMd]'
|
||||
})
|
||||
export class EditorMdDirective implements AfterViewInit {
|
||||
@Input() editormdConfig: EditorConfig; // 配置选项
|
||||
@Output() onEditorChange: EventEmitter<string> = new EventEmitter<string>(); // 发射器
|
||||
editor: any; // editormd编辑器
|
||||
|
||||
constructor(@Attribute('id') private id: string) {
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.editor = editormd(this.id, this.editormdConfig); // 创建编辑器
|
||||
|
||||
const out = this.onEditorChange;
|
||||
const textarea = $('#' + this.id + ' :first'); // 获取textarea元素
|
||||
|
||||
// 当编辑器内容改变时,触发textarea的change事件
|
||||
// tslint:disable-next-line:only-arrow-functions
|
||||
this.editor.on('change', function() {
|
||||
out.emit(textarea.val());
|
||||
});
|
||||
}
|
||||
}
|
||||
133
index/src/app/pages/write/write.component.css
Normal file
133
index/src/app/pages/write/write.component.css
Normal file
@@ -0,0 +1,133 @@
|
||||
/* @import '../../../../../assets/editormd/editor.css';
|
||||
|
||||
.fa {
|
||||
display: none;
|
||||
} */
|
||||
|
||||
h2 {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.con {
|
||||
display: flex;
|
||||
flex-wrap: wrap
|
||||
}
|
||||
|
||||
#title {
|
||||
/* 85% */
|
||||
flex-grow: 1;
|
||||
margin: 55px 0 5px 3%;
|
||||
height: 35px;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
#submit {
|
||||
margin: 55px 3% 5px 20px;
|
||||
width: 100px;
|
||||
height: 35px;
|
||||
background: #D83731;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
color: white
|
||||
}
|
||||
|
||||
#md {
|
||||
min-width: 1200px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
/*发布博客模态框设置*/
|
||||
|
||||
.am-modal-bd {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.article-tag,
|
||||
.article-type,
|
||||
.articleUrlHide {
|
||||
padding: 10px 20px 10px;
|
||||
}
|
||||
|
||||
.row {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.publish-tag {
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.tags,
|
||||
.type,
|
||||
.tag,
|
||||
.tag-inline,
|
||||
.categories,
|
||||
.grade,
|
||||
.Url,
|
||||
.tabloid {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.singleTag {
|
||||
background: #ececec;
|
||||
border-radius: 3px;
|
||||
margin: 0 2px;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.taginput {
|
||||
width: 300px;
|
||||
height: 32px;
|
||||
background: #fafafa;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.removeTag {
|
||||
margin-top: 10px;
|
||||
margin-left: 1px;
|
||||
font-size: 16px;
|
||||
height: 12px;
|
||||
color: #ddd;
|
||||
transition: color .3s ease-in;
|
||||
vertical-align: -1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.addTagsBtn {
|
||||
margin-left: 5px;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: #349edf;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
#select-type,
|
||||
#select-categories,
|
||||
#select-grade {
|
||||
width: 138px;
|
||||
height: 32px;
|
||||
background: #fafafa;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.required {
|
||||
margin-left: 2px;
|
||||
font-size: 12px;
|
||||
color: #ca0c16;
|
||||
}
|
||||
|
||||
#articleUrl {
|
||||
width: 300px;
|
||||
height: 32px;
|
||||
background: #fafafa;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
62
index/src/app/pages/write/write.component.html
Normal file
62
index/src/app/pages/write/write.component.html
Normal file
@@ -0,0 +1,62 @@
|
||||
<div style="margin-top: -80px;margin-bottom: -130px;z-index: 1000">
|
||||
<div class="con">
|
||||
<input type="text" [(ngModel)]="article.title" id="title">
|
||||
<button id="submit" (click)="articleSubmit()">提交</button>
|
||||
</div>
|
||||
|
||||
<div id="md" appEditorMd [editormdConfig]="conf" (onEditorChange)="syncModel($event)">
|
||||
<textarea style="display: block;" [(ngModel)]="article.mdContent"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nz-modal [(nzVisible)]="showPupup" nzTitle="发布博文" (nzOnCancel)="showPupup=false" (nzOnOk)="publishArticle()">
|
||||
<div class="am-modal-bd">
|
||||
<div class="article-setting">
|
||||
|
||||
<div class="article-type row">
|
||||
<label class="publish-tag">文章类型<strong>:</strong></label>
|
||||
<div class="type">
|
||||
<select id="select-type" [(ngModel)]="article.type">
|
||||
<option>请选择</option>
|
||||
<option [ngValue]="true">原创</option>
|
||||
<option [ngValue]="false">转载</option>
|
||||
</select>
|
||||
<span class="required">*</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="article-type row">
|
||||
<label class="publish-tag">博客分类<strong>:</strong></label>
|
||||
<div class="categories">
|
||||
<select id="select-categories" [(ngModel)]="article.category">
|
||||
<option class="categoriesOption" value="">请选择</option>
|
||||
<option class="categoriesOption" *ngFor="let category of categoryService.categories"
|
||||
value={{category.name}}>{{category.name}}</option>
|
||||
</select>
|
||||
<span class="required">*</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="article-tag row">
|
||||
<div class="tags">
|
||||
<div class="tag-inline">
|
||||
<label class="publish-tag">文章标签<strong>:</strong></label>
|
||||
<div class="tag">
|
||||
</div>
|
||||
<input class="taginput" placeholder="每个标签以英文','结束" [(ngModel)]="article.tags"><span
|
||||
class="required">*</span>
|
||||
</div>
|
||||
<span style="display: block;margin-left:67px;" *ngIf="article.tags">
|
||||
<span *ngFor="let tag of article.tags.split(',')" class="singleTag">{{tag}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="articleUrlHide row" *ngIf="!article.type">
|
||||
<label class="publish-tag" style="display: inline-block">原文链接<strong>:</strong></label>
|
||||
<div class="url" style="display: inline-block">
|
||||
<input type="text" id="articleUrl" [(ngModel)]="article.url" placeholder=" 请输入原文链接">
|
||||
<span class="required">*</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nz-modal>
|
||||
25
index/src/app/pages/write/write.component.spec.ts
Normal file
25
index/src/app/pages/write/write.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { WriteComponent } from './write.component';
|
||||
|
||||
describe('WriteComponent', () => {
|
||||
let component: WriteComponent;
|
||||
let fixture: ComponentFixture<WriteComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ WriteComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(WriteComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
183
index/src/app/pages/write/write.component.ts
Normal file
183
index/src/app/pages/write/write.component.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import {Component, OnInit, Output, Input, EventEmitter} from '@angular/core';
|
||||
import {EditorConfig} from '../../class/editor-config';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {Router} from '@angular/router';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {NzMessageService} from 'ng-zorro-antd';
|
||||
import {ArticleService} from '../../services/article/article.service';
|
||||
import {CategoryService} from '../../services/category/category.service';
|
||||
import {ArticleReq} from '../../class/articleReq';
|
||||
import {UserService} from '../../services/user/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-write',
|
||||
templateUrl: './write.component.html',
|
||||
styleUrls: ['./write.component.css']
|
||||
})
|
||||
export class WriteComponent implements OnInit {
|
||||
|
||||
constructor(public articleService: ArticleService,
|
||||
private routerinfo: ActivatedRoute,
|
||||
private router: Router,
|
||||
private titleService: Title,
|
||||
private message: NzMessageService,
|
||||
public categoryService: CategoryService,
|
||||
public userService: UserService) {
|
||||
titleService.setTitle('小海博客|创作');
|
||||
}
|
||||
|
||||
public obj: any;
|
||||
show = false;
|
||||
msg: any;
|
||||
conf = new EditorConfig();
|
||||
articleId;
|
||||
isUpdate = false;
|
||||
|
||||
public article: ArticleReq = new ArticleReq();
|
||||
|
||||
showPupup = false;
|
||||
|
||||
// 同步属性内容
|
||||
syncModel(str): void {
|
||||
this.article.mdContent = str;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.articleId = this.routerinfo.snapshot.queryParams.id;
|
||||
if (this.articleId != null) {
|
||||
this.isUpdate = true;
|
||||
this.getArticle();
|
||||
}
|
||||
if (!this.articleId && localStorage.getItem('tmpArticle')) {
|
||||
this.article = JSON.parse(localStorage.getItem('tmpArticle'));
|
||||
}
|
||||
this.setSuitableHeight();
|
||||
// 用户权限判断
|
||||
if (!this.userService.userInfo) {
|
||||
this.userService.getUserInfo().subscribe(data => {
|
||||
if (!data.result || data.result.role !== 'admin') {
|
||||
this.message.info('你暂时无发布文章的权限,所写文章将暂存在本地');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (this.userService.userInfo.role !== 'admin') {
|
||||
this.message.info('你暂时无发布文章的权限,所写文章将暂存在本地');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置高度
|
||||
*/
|
||||
setSuitableHeight() {
|
||||
this.conf.height = (window.innerHeight - 80 - 50) + '';
|
||||
}
|
||||
|
||||
// 提交按钮的事件
|
||||
articleSubmit() {
|
||||
if (this.article.title === '' || this.article.mdContent === '') {
|
||||
this.message.warning(this.article.title === '' ? '标题不能为空' : '文章不能为空');
|
||||
return;
|
||||
}
|
||||
this.showPupup = true;
|
||||
if (!this.categoryService.categories) {
|
||||
this.categoryService.getAllCategory();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章数据提交
|
||||
*/
|
||||
publishArticle() {
|
||||
// 去除空值
|
||||
this.article.tags = this.article.tags.split(',').filter(item => item !== '').toString();
|
||||
if (this.article.tags === '') {
|
||||
this.message.warning('文章标签不能为空');
|
||||
return;
|
||||
}
|
||||
if (this.article.type !== (true || false)) {
|
||||
this.message.warning('文章类型不能为空');
|
||||
return;
|
||||
}
|
||||
if (this.article.category === '') {
|
||||
this.message.warning('文章分类不能为空');
|
||||
return;
|
||||
}
|
||||
if (!this.article.type) {
|
||||
const regExp = /^http(s|):\/\/([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?$/;
|
||||
if (regExp.test(this.article.url)) {
|
||||
this.message.warning('原文链接不合法');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 文章 暂存
|
||||
localStorage.setItem('tmpArticle', JSON.stringify(this.article));
|
||||
|
||||
this.article.url = this.article.type ? null : this.article.url;
|
||||
|
||||
if (!this.isUpdate) {
|
||||
// 非文章更新
|
||||
this.articleService.createArticle(this.article).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
// TODO 成功
|
||||
this.message.success('发布成功,即将转跳');
|
||||
localStorage.removeItem('tmpArticle');
|
||||
|
||||
setTimeout(() => {
|
||||
this.router.navigateByUrl('article/' + data.result.id);
|
||||
}, 2500);
|
||||
}
|
||||
if (data.code === 301) {
|
||||
// 未登陆
|
||||
this.router.navigateByUrl('login');
|
||||
}
|
||||
if (data.code === 302) {
|
||||
this.message.error('你没有发布文章的权限');
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// 文章更新
|
||||
this.articleService.updateArticle(this.article).subscribe(data => {
|
||||
// TODO 未登陆
|
||||
if (data.code === 0) {
|
||||
this.message.success('更新成功,即将转跳');
|
||||
localStorage.removeItem('tmpArticle');
|
||||
setTimeout(() => {
|
||||
this.router.navigateByUrl('article/' + data.result.id);
|
||||
}, 2500);
|
||||
} else if (data.code === 301) {
|
||||
this.router.navigateByUrl('login');
|
||||
} else if (data.code === 302) {
|
||||
this.message.error('你没有更新文章的权限');
|
||||
} else {
|
||||
this.message.error('失败,原因:' + data.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文章 for update
|
||||
*/
|
||||
getArticle() {
|
||||
this.articleService.getArticleById(this.articleId, true).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.article.category = data.result.category;
|
||||
this.article.mdContent = data.result.mdContent;
|
||||
this.article.tags = String(data.result.tags);
|
||||
this.article.type = data.result.original;
|
||||
this.article.url = data.result.url;
|
||||
this.article.title = data.result.title;
|
||||
this.article.id = data.result.id;
|
||||
} else if (data.code === 201) {
|
||||
// 文章不存在
|
||||
this.message.error('文章不存在');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user