从"Blog"仓库中分离出来

This commit is contained in:
小海
2019-11-28 19:26:45 +08:00
commit c2aaf280db
616 changed files with 104128 additions and 0 deletions

View 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;
}
}

View 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>&nbsp;&nbsp;上一篇文章:{{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>&nbsp;&nbsp;下一篇文章篇文章:{{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>

View 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();
});
});

View 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;
}
}
}