重构界面ui
This commit is contained in:
12
index/src/app/api/api.service.spec.ts
Normal file
12
index/src/app/api/api.service.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {ApiService} from './api.service';
|
||||
|
||||
describe('ApiService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: ApiService = TestBed.get(ApiService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
345
index/src/app/api/api.service.ts
Normal file
345
index/src/app/api/api.service.ts
Normal file
@@ -0,0 +1,345 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Article} from '../class/Article';
|
||||
import {HttpService} from './http/http.service';
|
||||
import {PageList} from '../class/pageList';
|
||||
import {ErrDispatch} from '../class/ErrDispatch';
|
||||
import {ArticleReq} from '../class/ArticleReq';
|
||||
import {Category} from '../class/Category';
|
||||
import {Comment} from '../class/Comment';
|
||||
import {CommentReq} from '../class/CommentReq';
|
||||
import {Link} from '../class/Link';
|
||||
import {User} from '../class/User';
|
||||
import {LoginReq} from '../class/LoginReq';
|
||||
import {Observable, Observer, of} from 'rxjs';
|
||||
import {Response} from '../class/Response';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {LocalStorageService} from '../utils/local-storage.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ApiService extends HttpService {
|
||||
|
||||
constructor(httpClient: HttpClient,
|
||||
localStorageService: LocalStorageService) {
|
||||
super(httpClient, localStorageService);
|
||||
}
|
||||
|
||||
setErrDispatch(errDispatch: ErrDispatch) {
|
||||
super.setErrDispatch(errDispatch);
|
||||
}
|
||||
|
||||
createArticle(article: ArticleReq) {
|
||||
article.id = null;
|
||||
return super.Service<Article>({
|
||||
path: '/admin/article/create',
|
||||
contentType: 'application/json',
|
||||
method: 'POST',
|
||||
data: article
|
||||
});
|
||||
}
|
||||
|
||||
articles(pageNumber: number = 1, pageSize: number = 5) {
|
||||
return super.Service<PageList<Article>>({
|
||||
path: '/articles',
|
||||
method: 'GET',
|
||||
queryParam: {
|
||||
page: pageNumber,
|
||||
count: pageSize
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateArticle(article: ArticleReq) {
|
||||
return super.Service<Article>({
|
||||
path: '/admin/article/update',
|
||||
method: 'PUT',
|
||||
contentType: 'application/json',
|
||||
data: article
|
||||
});
|
||||
}
|
||||
|
||||
getArticle(articleId: number, is4Update: boolean = false) {
|
||||
return super.Service<Article>({
|
||||
path: `/article/articleID/${articleId}`,
|
||||
method: 'GET',
|
||||
queryParam: {update: is4Update},
|
||||
});
|
||||
}
|
||||
|
||||
articlesByCategory(category: string, pageNumber: number = 1, pageSize: number = 10) {
|
||||
return super.Service<PageList<Article>>({
|
||||
path: `/articles/category/${category}`,
|
||||
method: 'GET',
|
||||
queryParam: {
|
||||
page: pageNumber,
|
||||
count: pageSize
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
articlesByTag(tag: string, pageNumber: number = 1, pageSize: number = 10) {
|
||||
return super.Service<PageList<Article>>({
|
||||
path: `/articles/tag/${tag}`,
|
||||
method: 'GET',
|
||||
queryParam: {
|
||||
page: pageNumber,
|
||||
count: pageSize
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
categories() {
|
||||
return super.Service<Category[]>({
|
||||
path: '/categories',
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
|
||||
tags(pageNumber: number = 1, pageSize: number = 10) {
|
||||
return super.Service<Category[]>({
|
||||
path: '/tags',
|
||||
method: 'GET',
|
||||
queryParam: {
|
||||
page: pageNumber,
|
||||
count: pageSize
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tagsNac() {
|
||||
return super.Service<{ name: string, size: number }[]>({
|
||||
path: '/tags/nac',
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
|
||||
getCommentByPid(pid: number, pageNumber: number = 1, pageSize: number = 10) {
|
||||
return super.Service<PageList<Comment>[]>({
|
||||
path: `/comment/pid/${pid}`,
|
||||
method: 'GET',
|
||||
queryParam: {
|
||||
page: pageNumber,
|
||||
count: pageSize
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
comments(articleID: number, pageSize: number = 10, pageNumber: number = 1) {
|
||||
return super.Service<PageList<Comment>>({
|
||||
path: '/comments',
|
||||
method: 'GET',
|
||||
queryParam: {
|
||||
articleId: articleID,
|
||||
count: pageSize,
|
||||
page: pageNumber
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
leaveMsg(pageSize: number = 10, pageNumber: number = 1) {
|
||||
return super.Service<PageList<Comment>>({
|
||||
path: '/leaveMsg',
|
||||
method: 'GET',
|
||||
queryParam: {
|
||||
count: pageSize,
|
||||
page: pageNumber
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createComment(commentReq: CommentReq) {
|
||||
return super.Service<Comment>({
|
||||
path: '/user/comment/create',
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: commentReq
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
counts() {
|
||||
return super.Service<{
|
||||
articleCount: number,
|
||||
visitorCount: number,
|
||||
categoryCount: number,
|
||||
leaveMsgCount: number,
|
||||
tagCount: number,
|
||||
commentCount: number
|
||||
}>({
|
||||
path: '/counts',
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
|
||||
applyLink(link: Link) {
|
||||
return super.Service<string>({
|
||||
path: '/apply',
|
||||
method: 'POST',
|
||||
queryParam: {
|
||||
name: link.name,
|
||||
url: link.url
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
links() {
|
||||
return super.Service<Link[]>({
|
||||
path: '/links',
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
verifyImgCode(codeStr: string) {
|
||||
return super.Service<string>({
|
||||
path: '/verCode',
|
||||
method: 'POST',
|
||||
queryParam: {code: codeStr}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
login(loginReq: LoginReq) {
|
||||
const observable = super.Service<User>({
|
||||
path: '/login',
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: loginReq
|
||||
});
|
||||
let observer: Observer<Response<User>>;
|
||||
const oob = new Observable<Response<User>>(o => observer = o);
|
||||
observable.subscribe({
|
||||
next: o => {
|
||||
if (o.code === 0) {
|
||||
// 登录成功
|
||||
this.localStorageService.setToken(o.result.token);
|
||||
this.localStorageService.setUser(o.result);
|
||||
}
|
||||
observer.next(o);
|
||||
observer.complete();
|
||||
},
|
||||
error: err => {
|
||||
observer.error(err);
|
||||
observer.complete();
|
||||
}
|
||||
});
|
||||
return oob;
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.localStorageService.clear();
|
||||
return super.Service<string>({
|
||||
path: '/logout',
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
registration(emailStr: string, pwd: string) {
|
||||
return super.Service<boolean>({
|
||||
path: '/registration',
|
||||
method: 'POST',
|
||||
queryParam: {
|
||||
email: emailStr,
|
||||
password: pwd
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resetPwd(idStr: string, emailStr: string, pwdStr: string) {
|
||||
return super.Service<string>({
|
||||
path: '/resetPwd',
|
||||
method: 'POST',
|
||||
queryParam: {
|
||||
verifyId: idStr,
|
||||
email: emailStr,
|
||||
pwd: pwdStr
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
emailVerify(idStr: string, emailStr: string) {
|
||||
return super.Service<void>({
|
||||
path: '/emailVerify',
|
||||
method: 'POST',
|
||||
queryParam: {
|
||||
verifyId: idStr,
|
||||
email: emailStr
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
sendResetPwdEmail(emailStr: string) {
|
||||
return super.Service<string>({
|
||||
path: '/sendResetPwdEmail',
|
||||
method: 'POST',
|
||||
queryParam: {email: emailStr}
|
||||
});
|
||||
}
|
||||
|
||||
sendVerifyEmail(emailStr: string) {
|
||||
return super.Service<string>({
|
||||
path: '/sendVerifyEmail',
|
||||
method: 'POST',
|
||||
queryParam: {email: emailStr}
|
||||
});
|
||||
}
|
||||
|
||||
userInfo() {
|
||||
// 判断本地缓存的用户信息是否符合要求,符合要求返回本地缓存
|
||||
const user = this.localStorageService.getUser();
|
||||
if (this.localStorageService.isLogin() && user && !this.localStorageService.checkNeedNet()) {
|
||||
return of<Response<User>>(new Response<User>(user));
|
||||
}
|
||||
// 不符合 请求网络数据并更新缓存
|
||||
const observable = super.Service<User>({
|
||||
path: '/user/userInfo',
|
||||
method: 'GET',
|
||||
});
|
||||
let observer: Observer<Response<User>>;
|
||||
const oob = new Observable<Response<User>>(o => observer = o);
|
||||
observable.subscribe({
|
||||
next: o => {
|
||||
this.localStorageService.setUser(o.result);
|
||||
observer.next(o);
|
||||
observer.complete();
|
||||
},
|
||||
error: err => {
|
||||
// console.debug('登录过期 token错误 等等');
|
||||
this.localStorageService.removeToken();
|
||||
observer.error(err);
|
||||
observer.complete();
|
||||
}
|
||||
});
|
||||
return oob;
|
||||
}
|
||||
|
||||
visit() {
|
||||
return super.Service<User>({
|
||||
path: '/visit',
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
|
||||
webUpdate() {
|
||||
return super.Service<{ id: number, info: string, time: string }[]>({
|
||||
path: '/webUpdate',
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
|
||||
lastestUpdateTime() {
|
||||
return super.Service<string>({
|
||||
path: '/lastestUpdateTime',
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
|
||||
bingPic() {
|
||||
return super.Service<string>({
|
||||
path: '/bingPic',
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
15
index/src/app/api/http/http.service.spec.ts
Normal file
15
index/src/app/api/http/http.service.spec.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {HttpService} from './http.service';
|
||||
|
||||
describe('HttpService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: HttpService = TestBed.get(HttpService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
it('should be valid', () => {
|
||||
const service: HttpService = TestBed.get(HttpService);
|
||||
});
|
||||
});
|
||||
127
index/src/app/api/http/http.service.ts
Normal file
127
index/src/app/api/http/http.service.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {RequestObj} from '../../class/Request';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {environment} from '../../../environments/environment';
|
||||
import {LocalStorageService} from '../../utils/local-storage.service';
|
||||
import {Response} from '../../class/Response';
|
||||
import {Observable, Observer, Subject} from 'rxjs';
|
||||
import {ErrDispatch} from '../../class/ErrDispatch';
|
||||
import {multicast} from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class HttpService {
|
||||
|
||||
constructor(private httpClient: HttpClient,
|
||||
protected localStorageService: LocalStorageService) {
|
||||
}
|
||||
|
||||
private errorDispatch: ErrDispatch;
|
||||
|
||||
setErrDispatch(errDispatch: ErrDispatch) {
|
||||
this.errorDispatch = errDispatch;
|
||||
}
|
||||
|
||||
Service<T>(request: RequestObj) {
|
||||
// 设置默认值
|
||||
request.contentType = request.contentType == null ? 'application/x-www-form-urlencoded' : 'application/json';
|
||||
request.header = {
|
||||
'Content-Type': request.contentType
|
||||
};
|
||||
const token = this.localStorageService.getToken();
|
||||
if (token != null) {
|
||||
request.header.Authorization = token;
|
||||
}
|
||||
request.path = this.checkUrl(request);
|
||||
|
||||
let observable: Observable<Response<T>>;
|
||||
switch (request.method) {
|
||||
case 'GET':
|
||||
observable = this.get<Response<T>>(request);
|
||||
break;
|
||||
case 'DELETE':
|
||||
observable = this.delete<Response<T>>(request);
|
||||
break;
|
||||
case 'PUT':
|
||||
observable = this.put<Response<T>>(request);
|
||||
break;
|
||||
case 'POST':
|
||||
observable = this.post<Response<T>>(request);
|
||||
break;
|
||||
}
|
||||
|
||||
let observer: Observer<Response<T>>;
|
||||
|
||||
const oob = new Observable<Response<T>>(o => observer = o);
|
||||
|
||||
observable.subscribe(o => {
|
||||
if (o.code) {
|
||||
observer.error(o);
|
||||
if (this.errorDispatch) {
|
||||
this.errorDispatch.errHandler(o.code, o.msg, request);
|
||||
}
|
||||
} else {
|
||||
observer.next(o);
|
||||
}
|
||||
observer.complete();
|
||||
});
|
||||
return oob;
|
||||
}
|
||||
|
||||
private get<T>(request: RequestObj) {
|
||||
return this.httpClient.get<T>(request.path,
|
||||
{
|
||||
headers: request.header,
|
||||
withCredentials: true
|
||||
});
|
||||
}
|
||||
|
||||
private post<T>(request: RequestObj) {
|
||||
return this.httpClient.post<T>(request.path, request.data,
|
||||
{
|
||||
headers: request.header,
|
||||
withCredentials: true
|
||||
});
|
||||
}
|
||||
|
||||
private put<T>(request: RequestObj) {
|
||||
return this.httpClient.put<T>(request.path, request.data,
|
||||
{
|
||||
headers: request.header,
|
||||
withCredentials: true
|
||||
});
|
||||
}
|
||||
|
||||
private delete<T>(request: RequestObj) {
|
||||
return this.httpClient.delete<T>(request.path,
|
||||
{
|
||||
headers: request.header,
|
||||
withCredentials: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并且处理拼接 URl
|
||||
* @param req Request
|
||||
*/
|
||||
private checkUrl(req: RequestObj): string {
|
||||
let tmpUrl = environment.host;
|
||||
if (req.path.length === 0) {
|
||||
return tmpUrl;
|
||||
}
|
||||
if (req.path.substr(0, 1) !== '/') {
|
||||
tmpUrl += '/';
|
||||
}
|
||||
let queryStr = '';
|
||||
const keys = req.queryParam == null ? [] : Object.keys(req.queryParam);
|
||||
if (keys.length === 0) {
|
||||
return tmpUrl + req.path;
|
||||
}
|
||||
for (const key of keys) {
|
||||
queryStr += '&' + key + '=' + req.queryParam[key];
|
||||
}
|
||||
queryStr = queryStr.substr(1, queryStr.length);
|
||||
return tmpUrl + req.path + '?' + queryStr;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,32 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {Routes, RouterModule} from '@angular/router';
|
||||
|
||||
import {IndexComponent} from './pages/index/index.component';
|
||||
import {UpdateComponent} from './pages/update/update.component';
|
||||
import {CategoryComponent} from './pages/categories/category.component';
|
||||
import {TagComponent} from './pages/tag/tag.component';
|
||||
import {LeaveMsgComponent} from './pages/leave-msg/leave-msg.component';
|
||||
import {PartnerSitesComponent} from './pages/partner-sites/partner-sites.component';
|
||||
import {ArticleComponent} from './pages/article/article.component';
|
||||
import {EmailVerifyComponent} from './pages/email-verify/email-verify.component';
|
||||
import {ResetPwdComponent} from './pages/reset-pwd/reset-pwd.component';
|
||||
import {NotFoundComponent} from './pages/not-found/not-found.component';
|
||||
import {WriteComponent} from './pages/write/write.component';
|
||||
import {LoginComponent} from './pages/login/login.component';
|
||||
import {RegistrationComponent} from './pages/registration/registration.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '', component: IndexComponent},
|
||||
{path: 'update', component: UpdateComponent},
|
||||
{path: 'category', component: CategoryComponent},
|
||||
{path: 'tag', component: TagComponent},
|
||||
{path: 'leaveMsg', component: LeaveMsgComponent},
|
||||
{path: 'links', component: PartnerSitesComponent},
|
||||
{path: 'article/:id', component: ArticleComponent},
|
||||
{path: 'write', component: WriteComponent},
|
||||
{path: 'resetPwd', component: ResetPwdComponent},
|
||||
{path: 'emailVerify', component: EmailVerifyComponent},
|
||||
{path: 'login', component: LoginComponent},
|
||||
{path: 'registration', component: RegistrationComponent},
|
||||
{path: '404', component: NotFoundComponent},
|
||||
{path: '**', component: NotFoundComponent}
|
||||
];
|
||||
|
||||
// TODO lazyLoad
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule {
|
||||
}
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '', pathMatch: 'full', loadChildren: () => import('./view/index/index.module').then(mod => mod.IndexModule)},
|
||||
{path: 'update', loadChildren: () => import('./view/update/update.module').then(mod => mod.UpdateModule)},
|
||||
{path: 'article/:id', loadChildren: () => import('./view/article/article.module').then(mod => mod.ArticleModule)},
|
||||
{path: 'tags', loadChildren: () => import('./view/tag/tag.module').then(mod => mod.TagModule)},
|
||||
{path: 'categories', loadChildren: () => import('./view/category/category.module').then(mod => mod.CategoryModule)},
|
||||
// {path: 'leaveMsg', loadChildren: () => import('./view/leave-msg/leave-msg.module').then(mod => mod.LeaveMsgModule)},
|
||||
{path: 'emailVerify', loadChildren: () => import('./view/email-verify/email-verify.module').then(mod => mod.EmailVerifyModule)},
|
||||
{path: 'resetPwd', loadChildren: () => import('./view/reset-pwd/reset-pwd.module').then(mod => mod.ResetPwdModule)},
|
||||
{path: 'write', loadChildren: () => import('./view/write/write.module').then(mod => mod.WriteModule)},
|
||||
{path: 'links', loadChildren: () => import('./view/link/link.module').then(mod => mod.LinkModule)},
|
||||
{
|
||||
path: 'user', loadChildren: () => import('./view/login-registration/login-registration.module')
|
||||
.then(mod => mod.LoginRegistrationModule)
|
||||
},
|
||||
{path: '**', loadChildren: () => import('./view/page-not-found/page-not-found.module').then(mod => mod.PageNotFoundModule)}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot(routes)
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
|
||||
export class AppRoutingModule {
|
||||
}
|
||||
|
||||
@@ -1,21 +1,31 @@
|
||||
<app-header></app-header>
|
||||
<div style="height: 80px"></div>
|
||||
<app-header (loginEvent)="login()" (registrationEvent)="registration()" #headerComponent></app-header>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<div style="height: 60px"></div>
|
||||
<app-footer></app-footer>
|
||||
|
||||
<nz-modal [(nzVisible)]="userService.loginModalVisible" nzClosable="true"
|
||||
(nzOnCancel)="userService.loginModalVisible=false" [nzFooter]="footerButton">
|
||||
<ng-template [ngIf]="userService.loginModalType=='registration'">
|
||||
<app-registration></app-registration>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="userService.loginModalType=='login'">
|
||||
<app-login></app-login>
|
||||
</ng-template>
|
||||
|
||||
<nz-back-top [nzTemplate]="backToTop"></nz-back-top>
|
||||
|
||||
<ng-template #backToTop>
|
||||
<button style=" height: 60px;width: 60px;border-radius: 50%;" nz-button title="回到顶部">
|
||||
<i nz-icon nzType="rocket" nzTheme="outline"></i>
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
|
||||
<nz-modal
|
||||
[(nzVisible)]="loginModal" (nzOnCancel)="loginModal = !loginModal"
|
||||
[nzContent]="content" [nzFooter]="null" nzWidth="400">
|
||||
<ng-template #content>
|
||||
<c-login (loginStatus)="loginStatus($event)" [showSendEmail]="false"></c-login>
|
||||
</ng-template>
|
||||
</nz-modal>
|
||||
|
||||
<ng-template #footerButton>
|
||||
<small style="width:100%;text-align: center">小海博客</small>
|
||||
</ng-template>
|
||||
<nz-modal
|
||||
[(nzVisible)]="regModal" (nzOnCancel)="regModal = !regModal" [nzContent]="content2"
|
||||
[nzFooter]="null" nzWidth="400">
|
||||
<ng-template #content2>
|
||||
<c-registration (regStatus)="regModal = !$event" (regAccount)="cons($event)"></c-registration>
|
||||
</ng-template>
|
||||
</nz-modal>
|
||||
20
index/src/app/app.component.less
Normal file
20
index/src/app/app.component.less
Normal file
@@ -0,0 +1,20 @@
|
||||
#slider-container {
|
||||
width: 400px;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
overflow: hidden;
|
||||
background: #4ffff5;
|
||||
z-index: 100;
|
||||
padding: 18px;
|
||||
border: 1px solid #ddd;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#article-container {
|
||||
padding-left: 0;
|
||||
padding-right: 430px;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -1,35 +1,35 @@
|
||||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'blog'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.title).toEqual('blog');
|
||||
});
|
||||
|
||||
it('should render title in a h1 tag', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('h1').textContent).toContain('Welcome to blog!');
|
||||
});
|
||||
});
|
||||
import {TestBed, async} from '@angular/core/testing';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {AppComponent} from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'index'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.title).toEqual('index');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('.content span').textContent).toContain('index app is running!');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,38 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {UserService} from './services/user/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'blog';
|
||||
|
||||
constructor(public userService: UserService) {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
import {Component, ElementRef, OnInit, TemplateRef, ViewChild} from '@angular/core';
|
||||
import {LoginReq} from './class/LoginReq';
|
||||
import {HeaderComponent} from './components/header/header.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.less']
|
||||
})
|
||||
export class AppComponent {
|
||||
loginModal: boolean = false;
|
||||
regModal: boolean = false;
|
||||
@ViewChild('headerComponent') header: HeaderComponent;
|
||||
|
||||
registration() {
|
||||
// todo :: 登录
|
||||
console.log('registration');
|
||||
this.regModal = true;
|
||||
|
||||
}
|
||||
|
||||
login() {
|
||||
// TODO :: 注册
|
||||
console.log('login');
|
||||
this.loginModal = true;
|
||||
}
|
||||
|
||||
cons($event: LoginReq) {
|
||||
console.log($event);
|
||||
}
|
||||
|
||||
loginStatus(e: boolean) {
|
||||
if (e) {
|
||||
this.header.getInfo();
|
||||
}
|
||||
this.loginModal = !e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,92 +1,38 @@
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
import {AppRoutingModule} from './app-routing.module';
|
||||
import {AppComponent} from './app.component';
|
||||
import {NgZorroAntdModule, NZ_I18N, zh_CN} from 'ng-zorro-antd';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {registerLocaleData} from '@angular/common';
|
||||
import zh from '@angular/common/locales/zh';
|
||||
|
||||
import {EditorMdDirective} from './pages/write/editor/editor-md.directive';
|
||||
|
||||
// components
|
||||
import {HeaderComponent} from './components/header/header.component';
|
||||
import {FooterComponent} from './components/footer/footer.component';
|
||||
|
||||
// pages
|
||||
import {IndexComponent} from './pages/index/index.component';
|
||||
import {UpdateComponent} from './pages/update/update.component';
|
||||
import {CategoryComponent} from './pages/categories/category.component';
|
||||
import {TagComponent} from './pages/tag/tag.component';
|
||||
import {LeaveMsgComponent} from './pages/leave-msg/leave-msg.component';
|
||||
import {PartnerSitesComponent} from './pages/partner-sites/partner-sites.component';
|
||||
import {ArticleComponent} from './pages/article/article.component';
|
||||
import {EmailVerifyComponent} from './pages/email-verify/email-verify.component';
|
||||
import {ResetPwdComponent} from './pages/reset-pwd/reset-pwd.component';
|
||||
import {NotFoundComponent} from './pages/not-found/not-found.component';
|
||||
import {LoginComponent} from './pages/login/login.component';
|
||||
import {RegistrationComponent} from './pages/registration/registration.component';
|
||||
import {WriteComponent} from './pages/write/write.component';
|
||||
|
||||
// services
|
||||
import {HttpService} from './services/http.service';
|
||||
import {UserService} from './services/user/user.service';
|
||||
import {ArticleService} from './services/article/article.service';
|
||||
import {CategoryService} from './services/category/category.service';
|
||||
import {CountService} from './services/count/count.service';
|
||||
import {CommentService} from './services/comment/comment.service';
|
||||
import {TagService} from './services/tag/tag.service';
|
||||
import {WebUpdateService} from './services/update/web-update.service';
|
||||
import {LinkService} from './services/link/link.service';
|
||||
|
||||
registerLocaleData(zh);
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
HeaderComponent,
|
||||
FooterComponent,
|
||||
EditorMdDirective,
|
||||
|
||||
IndexComponent,
|
||||
UpdateComponent,
|
||||
CategoryComponent,
|
||||
TagComponent,
|
||||
LeaveMsgComponent,
|
||||
PartnerSitesComponent,
|
||||
ArticleComponent,
|
||||
EmailVerifyComponent,
|
||||
ResetPwdComponent,
|
||||
NotFoundComponent,
|
||||
LoginComponent,
|
||||
RegistrationComponent,
|
||||
WriteComponent
|
||||
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
NgZorroAntdModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule
|
||||
],
|
||||
providers: [
|
||||
{provide: NZ_I18N, useValue: zh_CN},
|
||||
HttpService,
|
||||
UserService,
|
||||
ArticleService,
|
||||
CountService,
|
||||
CommentService,
|
||||
TagService,
|
||||
WebUpdateService,
|
||||
CategoryService,
|
||||
LinkService
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {AppComponent} from './app.component';
|
||||
import {NgZorroAntdModule, NZ_I18N, zh_CN} from 'ng-zorro-antd';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {registerLocaleData} from '@angular/common';
|
||||
import zh from '@angular/common/locales/zh';
|
||||
import {HeaderComponent} from './components/header/header.component';
|
||||
import {FooterComponent} from './components/footer/footer.component';
|
||||
import {AppRoutingModule} from './app-routing.module';
|
||||
import {LoginRegistrationModule} from './view/login-registration/login-registration.module';
|
||||
|
||||
|
||||
registerLocaleData(zh);
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
HeaderComponent,
|
||||
FooterComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
NgZorroAntdModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule,
|
||||
LoginRegistrationModule
|
||||
],
|
||||
providers: [{provide: NZ_I18N, useValue: zh_CN}],
|
||||
exports: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
|
||||
12
index/src/app/class/Comment.ts
Normal file
12
index/src/app/class/Comment.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export class Comment {
|
||||
id?: number;
|
||||
authorName?: string;
|
||||
authorAvatarImgUrl?: string;
|
||||
content: string;
|
||||
articleID: number;
|
||||
date?: string;
|
||||
responseId: string;
|
||||
pid: number;
|
||||
comment: boolean;
|
||||
respComment: Comment[];
|
||||
}
|
||||
27
index/src/app/class/EditorConfig.ts
Normal file
27
index/src/app/class/EditorConfig.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {environment} from '../../environments/environment';
|
||||
|
||||
export class EditorConfig {
|
||||
public width = '100%';
|
||||
public height = '400';
|
||||
public path = 'assets/editor/lib/';
|
||||
public codeFold: true;
|
||||
public searchReplace = true;
|
||||
public toolbar = true;
|
||||
public placeholder = '欢迎来到小海的创作中心';
|
||||
public emoji = true;
|
||||
public taskList = true;
|
||||
public tex = true;
|
||||
public readOnly = false;
|
||||
public tocm = true;
|
||||
public watch = true;
|
||||
public previewCodeHighlight = true;
|
||||
public saveHTMLToTextarea = true;
|
||||
public markdown = '';
|
||||
public flowChart = true;
|
||||
public syncScrolling = true;
|
||||
public sequenceDiagram = true;
|
||||
public imageUpload = true;
|
||||
public imageFormats = ['jpg', 'jpeg', 'gif', 'png', 'bmp', 'webp'];
|
||||
public imageUploadURL = environment.host + '/imgUpload';
|
||||
|
||||
}
|
||||
5
index/src/app/class/ErrDispatch.ts
Normal file
5
index/src/app/class/ErrDispatch.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import {RequestObj} from './Request';
|
||||
|
||||
export interface ErrDispatch {
|
||||
errHandler(code: number, msg: string, request?: RequestObj): void;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export class LeaveMsg {
|
||||
id: number;
|
||||
type: number;
|
||||
authorName: string;
|
||||
authorAvatarImgUrl: string;
|
||||
content: string;
|
||||
date: string;
|
||||
pid: number;
|
||||
responseId: string;
|
||||
child: LeaveMsg[];
|
||||
}
|
||||
5
index/src/app/class/Link.ts
Normal file
5
index/src/app/class/Link.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export class Link {
|
||||
id?: number;
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
12
index/src/app/class/Request.ts
Normal file
12
index/src/app/class/Request.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {HttpHeaders} from '@angular/common/http';
|
||||
|
||||
export class RequestObj {
|
||||
path: string;
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
||||
data?: {};
|
||||
contentType?: 'application/json' | 'application/x-www-form-urlencoded';
|
||||
queryParam?: {};
|
||||
header?: HttpHeaders | {
|
||||
[header: string]: string | string[];
|
||||
};
|
||||
}
|
||||
11
index/src/app/class/Response.ts
Normal file
11
index/src/app/class/Response.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export class Response<T> {
|
||||
code: number;
|
||||
msg: string;
|
||||
result: T;
|
||||
date: number;
|
||||
|
||||
constructor(t: T) {
|
||||
this.code = 0;
|
||||
this.result = t;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
export class Article {
|
||||
id: number;
|
||||
title: string;
|
||||
summary: string;
|
||||
mdContent?: string;
|
||||
original?: boolean;
|
||||
url?: string;
|
||||
publishDateFormat?: string;
|
||||
updateDateFormat?: string;
|
||||
category?: string;
|
||||
tags?: string[];
|
||||
authorName?: string;
|
||||
preArticleId?: number;
|
||||
nextArticleId?: number;
|
||||
preArticleTitle?: string;
|
||||
nextArticleTitle?: string;
|
||||
readingNumber?: number;
|
||||
open?: string;
|
||||
id: number;
|
||||
title: string;
|
||||
summary: string;
|
||||
mdContent?: string;
|
||||
original?: boolean;
|
||||
url?: string;
|
||||
publishDateFormat?: string;
|
||||
updateDateFormat?: string;
|
||||
category?: string;
|
||||
tags?: string[];
|
||||
authorName?: string;
|
||||
preArticleId?: number;
|
||||
nextArticleId?: number;
|
||||
preArticleTitle?: string;
|
||||
nextArticleTitle?: string;
|
||||
readingNumber?: number;
|
||||
open?: string;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export class ArticleReq {
|
||||
category: string;
|
||||
id?: number;
|
||||
mdContent: string;
|
||||
open: boolean;
|
||||
tags: string;
|
||||
title: string;
|
||||
type: boolean;
|
||||
url?: string;
|
||||
category: string;
|
||||
id?: number;
|
||||
mdContent: string;
|
||||
open: boolean;
|
||||
tags: string;
|
||||
title: string;
|
||||
type: boolean;
|
||||
url?: string;
|
||||
|
||||
constructor() {
|
||||
this.type = true;
|
||||
}
|
||||
constructor() {
|
||||
this.type = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export class Category {
|
||||
id: number;
|
||||
name: string;
|
||||
articles: string;
|
||||
size?: number;
|
||||
id: number;
|
||||
name: string;
|
||||
articles?: number[];
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
export class CommentReq {
|
||||
articleID: number;
|
||||
comment: boolean;
|
||||
content: string;
|
||||
id: number;
|
||||
pid: number;
|
||||
responseId: string;
|
||||
id?: number;
|
||||
comment: boolean;
|
||||
content: string;
|
||||
pid: number;
|
||||
articleID: number;
|
||||
responseId: string;
|
||||
|
||||
constructor(comment: boolean) {
|
||||
this.comment = comment;
|
||||
this.responseId = '';
|
||||
if (!comment) {
|
||||
this.articleID = -1;
|
||||
constructor(comment: boolean) {
|
||||
this.comment = comment;
|
||||
this.responseId = '';
|
||||
if (!comment) {
|
||||
this.articleID = -1;
|
||||
}
|
||||
this.pid = -1;
|
||||
this.id = null;
|
||||
}
|
||||
this.pid = -1;
|
||||
this.id = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export class Count {
|
||||
visitorCount: number = 0;
|
||||
leaveMsgCount: number = 0;
|
||||
tagCount: number = 0;
|
||||
articleCount: number = 0;
|
||||
commentCount: number = 0;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export class Data {
|
||||
code: number;
|
||||
msg: string;
|
||||
result: any;
|
||||
date: number;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import {environment} from '../../environments/environment';
|
||||
|
||||
export class EditorConfig {
|
||||
public width = '100%';
|
||||
public height = '400';
|
||||
public path = 'assets/editor.md/lib/';
|
||||
public codeFold: true;
|
||||
public searchReplace = true;
|
||||
public toolbar = true;
|
||||
public placeholder = '欢迎来到小海的创作中心';
|
||||
public emoji = true;
|
||||
public taskList = true;
|
||||
public tex = true;
|
||||
public readOnly = false;
|
||||
public tocm = true;
|
||||
public watch = true;
|
||||
public previewCodeHighlight = true;
|
||||
public saveHTMLToTextarea = true;
|
||||
public markdown = '';
|
||||
public flowChart = true;
|
||||
public syncScrolling = true;
|
||||
public sequenceDiagram = true;
|
||||
public imageUpload = true;
|
||||
public imageFormats = ['jpg', 'jpeg', 'gif', 'png', 'bmp', 'webp'];
|
||||
public imageUploadURL = environment.host + '/imgUpload';
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
export class LoginReq {
|
||||
email: string;
|
||||
password: string;
|
||||
isRememberMe: boolean = false;
|
||||
}
|
||||
email: string;
|
||||
isRememberMe: boolean;
|
||||
password: string;
|
||||
|
||||
constructor(email: string, isRememberMe: boolean, password: string) {
|
||||
this.email = email;
|
||||
this.isRememberMe = isRememberMe;
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
export class Page<T> {
|
||||
total: number;
|
||||
list: T[];
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
size: number;
|
||||
startRow: number;
|
||||
endRow: number;
|
||||
pages: number;
|
||||
prePage: number;
|
||||
nextPage: number;
|
||||
isFirstPage: boolean;
|
||||
isLastPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
hasNextPage: boolean;
|
||||
navigatePages: number;
|
||||
navigatepageNums: number[];
|
||||
navigateFirstPage: number;
|
||||
navigateLastPage: number;
|
||||
}
|
||||
20
index/src/app/class/pageList.ts
Normal file
20
index/src/app/class/pageList.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export class PageList<T> {
|
||||
total: number;
|
||||
list: T[];
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
size: number;
|
||||
startRow: number;
|
||||
endRow: number;
|
||||
pages: number;
|
||||
prePage: number;
|
||||
nextPage: number;
|
||||
isFirstPage: boolean;
|
||||
isLastPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
hasNextPage: boolean;
|
||||
navigatePages: number;
|
||||
navigatepageNums: number[];
|
||||
navigateFirstPage: number;
|
||||
navigateLastPage: number;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
export class Tag {
|
||||
id?: number;
|
||||
name: string;
|
||||
articles?: string;
|
||||
size: number;
|
||||
id: number;
|
||||
name: string;
|
||||
articles?: number[];
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export class UpdateInfo {
|
||||
id: number;
|
||||
info: string;
|
||||
time: string;
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
export class User {
|
||||
id: number;
|
||||
email: string;
|
||||
displayName: string;
|
||||
emailStatus: boolean;
|
||||
avatarImgUrl: string;
|
||||
desc: string;
|
||||
role: string;
|
||||
id: number;
|
||||
email: string;
|
||||
displayName: string;
|
||||
emailStatus: boolean;
|
||||
avatarImgUrl: string;
|
||||
desc: string;
|
||||
role: string;
|
||||
token?: string;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<div class="footer">
|
||||
<hr>
|
||||
<div>
|
||||
<a href="http://www.miitbeian.gov.cn" target="_blank">
|
||||
鄂ICP备18023929号
|
||||
</a>
|
||||
<div>
|
||||
© 2019 <a href="https://www.celess.cn">小海博客</a> - <i class="fa fa-coffee" style="margin: 0 5px"></i>郑海 版权所有
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer" *ngIf="show">
|
||||
<nz-divider></nz-divider>
|
||||
<div>
|
||||
<a href="http://www.miitbeian.gov.cn" target="_blank">
|
||||
鄂ICP备18023929号
|
||||
</a>
|
||||
<div>
|
||||
© 2019 <a href="https://www.celess.cn">小海博客</a> -
|
||||
<span>郑海 </span> <span *ngIf="gName">& {{gName}} </span>版权所有
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,22 +1,22 @@
|
||||
.footer {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
background-color: #eeeeee;
|
||||
text-align: center;
|
||||
color: #009688;
|
||||
height: 50px;
|
||||
position: relative;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
/* 设置z-index 是为了write页面将footer隐藏*/
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #ffffff;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #009688 !important;
|
||||
}
|
||||
.footer {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
background-color: #eeeeee;
|
||||
text-align: center;
|
||||
color: #009688;
|
||||
height: 50px;
|
||||
position: relative;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
/* 设置z-index 是为了write页面将footer隐藏*/
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #ffffff;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #009688 !important;
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { FooterComponent } from './footer.component';
|
||||
|
||||
describe('FooterComponent', () => {
|
||||
let component: FooterComponent;
|
||||
let fixture: ComponentFixture<FooterComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ FooterComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FooterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { FooterComponent } from './footer.component';
|
||||
|
||||
describe('FooterComponent', () => {
|
||||
let component: FooterComponent;
|
||||
let fixture: ComponentFixture<FooterComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ FooterComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FooterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrls: ['./footer.component.css']
|
||||
})
|
||||
export class FooterComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {filter} from 'rxjs/operators';
|
||||
import {NavigationEnd, Route, Router, RouterEvent} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrls: ['./footer.component.less']
|
||||
})
|
||||
export class FooterComponent implements OnInit {
|
||||
show: boolean;
|
||||
|
||||
constructor(private router: Router) {
|
||||
this.show = true;
|
||||
}
|
||||
|
||||
readonly gName: string;
|
||||
|
||||
ngOnInit() {
|
||||
this.router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe((e: RouterEvent) => {
|
||||
const indexOf = e.url.lastIndexOf('/');
|
||||
const prefix = e.url.substr(0, indexOf === 0 ? e.url.length : indexOf);
|
||||
if (prefix === '/user' || prefix === '/write') {
|
||||
this.show = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
header {
|
||||
height: 50px;
|
||||
background-color: #ffffff;
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div {
|
||||
float: left;
|
||||
}
|
||||
|
||||
header img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
margin: 5px 5px 5px 30px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.headerButton {
|
||||
border: none;
|
||||
background: #3F66FF;
|
||||
color: #ffffff !important;
|
||||
margin-right: 10px;
|
||||
border-radius: 5px;
|
||||
padding: 8px 13px;
|
||||
line-height: 50px
|
||||
}
|
||||
|
||||
#landr {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#loged {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
#blogTitle,
|
||||
#desc {
|
||||
display: block;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#blogTitle :hover {
|
||||
text-decoration: none;
|
||||
color: #797979;
|
||||
}
|
||||
|
||||
#blogTitle {
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
#blogTitle :hover {
|
||||
font-size: 23px;
|
||||
}
|
||||
|
||||
#desc {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
[nz-button] {
|
||||
margin-right: 8px;
|
||||
margin-top: 9px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 768px) {
|
||||
/**导航栏*/
|
||||
.nav-desktop {
|
||||
position: absolute;
|
||||
width: auto;
|
||||
left: 50%;
|
||||
top: 0;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
.nav-phone {
|
||||
display: none
|
||||
}
|
||||
|
||||
a {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nav-desktop li {
|
||||
float: left;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.nav-desktop li:hover {
|
||||
background: #ececec;
|
||||
color: #797979;
|
||||
}
|
||||
|
||||
.nav-desktop li a {
|
||||
height: 100%;
|
||||
line-height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and ( max-width: 768px) {
|
||||
.nav-desktop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-phone {
|
||||
display: block;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.ul {
|
||||
width: 300px;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.ul li {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #ececec
|
||||
}
|
||||
|
||||
#click-main {
|
||||
line-height: 50px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +1,36 @@
|
||||
<header>
|
||||
<a href="/">
|
||||
<div>
|
||||
<img src="https://56462271.oss-cn-beijing.aliyuncs.com/web/logo.png"/>
|
||||
</div>
|
||||
</a>
|
||||
<div>
|
||||
<a href="/" id="blogTitle"><span>小海博客</span></a>
|
||||
<span id="desc">记录学习成长历程</span>
|
||||
</div>
|
||||
|
||||
|
||||
<ul class="nav-desktop">
|
||||
<li><a class="top_bar" routerLink="/" style="cursor: pointer;"><i nz-icon nzType="home" nzTheme="fill"></i> 首页</a></li>
|
||||
<li><a class="top_bar" routerLink="/category" style="cursor: pointer;"><i nz-icon nzType="project" nzTheme="fill"></i> 分类</a>
|
||||
</li>
|
||||
<li><a class="top_bar" routerLink="/tag" style="cursor: pointer;"><i nz-icon nzType="tags" nzTheme="fill"></i> 标签</a>
|
||||
</li>
|
||||
<li><a class="top_bar" routerLink="/leaveMsg" style="cursor: pointer;"><i nz-icon nzType="profile" nzTheme="fill"></i> 留言</a>
|
||||
</li>
|
||||
<li><a class="top_bar" routerLink="/update" style="cursor: pointer;"><i nz-icon nzType="up-square" nzTheme="fill"></i> 更新</a>
|
||||
</li>
|
||||
<li><a class="top_bar" routerLink="/links" style="cursor: pointer;"><i nz-icon nzType="link" nzTheme="outline"></i> 友链</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<div class="nav-phone">
|
||||
<a nz-dropdown [nzDropdownMenu]="menu" nzTrigger="click" [nzClickHide]="false" [(nzVisible)]="visibleOfMenu"
|
||||
id="click-main">
|
||||
<i nz-icon nzType="menu" nzTheme="outline"></i>
|
||||
</a>
|
||||
<nz-dropdown-menu #menu="nzDropdownMenu">
|
||||
<ul nz-menu class="ul">
|
||||
<li nz-menu-item><a class="top_bar" routerLink="/" style="cursor: pointer;"><i nz-icon nzType="home" nzTheme="fill"></i> 首页</a></li>
|
||||
<li nz-menu-item><a class="top_bar" routerLink="/category" style="cursor: pointer;"><i nz-icon nzType="project" nzTheme="fill"></i> 分类</a></li>
|
||||
<li nz-menu-item><a class="top_bar" routerLink="/tag" style="cursor: pointer;"><i nz-icon nzType="tags" nzTheme="fill"></i> 标签</a></li>
|
||||
<li nz-menu-item><a class="top_bar" routerLink="/leaveMsg" style="cursor: pointer;"><i nz-icon nzType="profile" nzTheme="fill"></i> 留言</a></li>
|
||||
<li nz-menu-item><a class="top_bar" routerLink="/update" style="cursor: pointer;"><i nz-icon nzType="up-square" nzTheme="fill"></i> 更新</a></li>
|
||||
<li nz-menu-item><a class="top_bar" routerLink="/links" style="cursor: pointer;"><i nz-icon nzType="link" nzTheme="outline"></i> 友链</a></li>
|
||||
</ul>
|
||||
</nz-dropdown-menu>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="loged" *ngIf="userService.userInfo">
|
||||
<a nz-dropdown [nzDropdownMenu]="menu" nzTrigger="click" [nzClickHide]="false" [(nzVisible)]="visible">
|
||||
{{userService.userInfo.displayName ? userService.userInfo.displayName : userService.userInfo.email}}
|
||||
<i nz-icon nzType="down"></i>
|
||||
</a>
|
||||
<nz-dropdown-menu #menu="nzDropdownMenu">
|
||||
<ul nz-menu>
|
||||
<li nz-menu-item><a href="/admin/">{{userService.userInfo.role == 'admin' ? '网站后台管理' : '个人中心'}}</a></li>
|
||||
<hr style="margin: 10px 0 5px 0;">
|
||||
<li nz-menu-item>
|
||||
<a (click)="logout()">退出登录</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nz-dropdown-menu>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="landr" *ngIf="!userService.userInfo">
|
||||
<button nz-button nzType="primary" (click)="userService.showModal('login')">登录</button>
|
||||
<button nz-button nzType="primary" (click)="userService.showModal('registration')">注册</button>
|
||||
</div>
|
||||
|
||||
</header>
|
||||
<div id="header" nz-row [class.containBg]="size =='large'">
|
||||
<div id="header-logo-title" nz-col nzSpan="6">
|
||||
<img src="https://56462271.oss-cn-beijing.aliyuncs.com/web/logo.jpg"
|
||||
alt="logo" id="logo">
|
||||
<span id="header-title"><a routerLink="/">小海博客</a></span>
|
||||
</div>
|
||||
<ul id="nav" nz-col nzSpan="12" [ngStyle]="{'display':showList?'block':'none'}">
|
||||
<ng-template *ngFor="let item of pageList" [ngIf]="item.show">
|
||||
<li (click)="dealLink(item.path)" style="user-select: none;cursor: pointer">
|
||||
<i nz-icon [nzType]="item.icon" [nzTheme]="item.iconType"></i>
|
||||
{{item.name}}
|
||||
</li>
|
||||
</ng-template>
|
||||
</ul>
|
||||
<div id="header-user-login" *ngIf="!userInfo">
|
||||
<button nz-button nzType="primary" (click)="login()">登录</button>
|
||||
<button nz-button nzType="primary" (click)="registration()">注册</button>
|
||||
</div>
|
||||
<div *ngIf="userInfo" id="info">
|
||||
<img [src]="userInfo.avatarImgUrl" alt="avatar" id="avatar">
|
||||
<button nz-button nzType="link" class="info-name"
|
||||
nz-dropdown [nzDropdownMenu]="menu" nzPlacement="bottomRight" nzTrigger="click">
|
||||
{{userInfo.displayName}}<i nz-icon nzType="caret-down" nzTheme="outline"></i>
|
||||
</button>
|
||||
<nz-dropdown-menu #menu="nzDropdownMenu">
|
||||
<ul nz-menu nzSelectable>
|
||||
<li nz-menu-item (click)="toAdminPage()"><i nz-icon nzType="info-circle" nzTheme="outline"></i>管理后台</li>
|
||||
<hr style="opacity: 0.5">
|
||||
<li nz-menu-item (click)="logout()"><i nz-icon nzType="logout" nzTheme="outline"></i>注销登录</li>
|
||||
</ul>
|
||||
</nz-dropdown-menu>
|
||||
</div>
|
||||
<button id="header-menu" nz-button [style.top.px]="size=='large'?25:10" (click)="changeMenuStatus()">
|
||||
<i nz-icon [nzType]="showList?'arrow-left':'menu'" nzTheme="outline"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
160
index/src/app/components/header/header.component.less
Normal file
160
index/src/app/components/header/header.component.less
Normal file
@@ -0,0 +1,160 @@
|
||||
@import "../../global-variables";
|
||||
|
||||
.containBg {
|
||||
background-image: url("src/assets/img/bg_header.jpg") !important;
|
||||
color: #ffffff !important;
|
||||
min-height: @header-min-height;
|
||||
background-position: center center !important;
|
||||
background-size: cover !important;
|
||||
opacity: 1 !important;
|
||||
padding-top: 15px !important;
|
||||
|
||||
.info-name {
|
||||
color: white !important;
|
||||
}
|
||||
a {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#header {
|
||||
// 设置header背景
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
color: black;
|
||||
font-family: -apple-system, BlinkMacSystemFont, opensans, Optima, "Microsoft Yahei", sans-serif;
|
||||
padding-top: 0px;
|
||||
position: relative;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
|
||||
div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#avatar {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #bdbdbd;
|
||||
}
|
||||
|
||||
#logo {
|
||||
// 设置logo的宽高
|
||||
width: @header-logo-width;
|
||||
height: @header-logo-height;
|
||||
border-radius: 50%;
|
||||
//position: relative;
|
||||
//top: 0;
|
||||
//left: 30px;
|
||||
}
|
||||
|
||||
#header-title {
|
||||
//position: relative;
|
||||
//top: 0;
|
||||
//left: @logo_wh;
|
||||
font-size: xx-large;
|
||||
font-weight: lighter;
|
||||
min-width: 130px;
|
||||
max-width: 50%;
|
||||
vertical-align: middle;
|
||||
|
||||
a {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
#header-user-login, #info {
|
||||
float: right;
|
||||
margin-right: 15px;
|
||||
line-height: 50px;
|
||||
|
||||
|
||||
[nz-button] {
|
||||
margin-right: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.info-name {
|
||||
color: black;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
#header-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#nav {
|
||||
@item-width: 80px;
|
||||
width: auto;
|
||||
list-style: none;
|
||||
|
||||
li:hover {
|
||||
background: #ececec;
|
||||
//color: black;
|
||||
}
|
||||
|
||||
li {
|
||||
float: left;
|
||||
height: 50px;
|
||||
width: @item-width;
|
||||
line-height: 50px;
|
||||
//color: white;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* max-width:910px */
|
||||
@media screen and (max-width: @max-width) {
|
||||
#header {
|
||||
#header-logo-title {
|
||||
position: static;
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
#nav {
|
||||
display: none;
|
||||
background: white;
|
||||
opacity: 0.5;
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
|
||||
|
||||
li {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
line-height: 40px;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
color: black;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
background: #bce672;
|
||||
}
|
||||
}
|
||||
|
||||
#header-user-login {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#header-menu {
|
||||
display: block !important;
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
top: 25px
|
||||
}
|
||||
|
||||
#info {
|
||||
display: block !important;
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
top: 15px
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HeaderComponent } from './header.component';
|
||||
|
||||
describe('HeaderComponent', () => {
|
||||
let component: HeaderComponent;
|
||||
let fixture: ComponentFixture<HeaderComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ HeaderComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HeaderComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HeaderComponent } from './header.component';
|
||||
|
||||
describe('HeaderComponent', () => {
|
||||
let component: HeaderComponent;
|
||||
let fixture: ComponentFixture<HeaderComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ HeaderComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HeaderComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,28 +1,149 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {UserService} from '../../services/user/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.css']
|
||||
})
|
||||
export class HeaderComponent implements OnInit {
|
||||
|
||||
|
||||
constructor(public userService: UserService) {
|
||||
}
|
||||
|
||||
// 菜单是否可见
|
||||
public visible: boolean = false;
|
||||
// 导航是否可见(手机显示时)
|
||||
public visibleOfMenu: boolean = false;
|
||||
|
||||
ngOnInit() {
|
||||
this.userService.getUserInfo();
|
||||
this.userService.http.visit();
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.userService.logout();
|
||||
}
|
||||
}
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {windowWidthChange} from '../../utils/util';
|
||||
import {NavigationEnd, Router, RouterEvent} from '@angular/router';
|
||||
import {filter} from 'rxjs/operators';
|
||||
import {ApiService} from '../../api/api.service';
|
||||
import {User} from '../../class/User';
|
||||
import {LocalStorageService} from '../../utils/local-storage.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.less']
|
||||
})
|
||||
export class HeaderComponent implements OnInit {
|
||||
|
||||
constructor(private router: Router,
|
||||
private apiService: ApiService) {
|
||||
this.pageList = [
|
||||
{name: '首页', path: '/', icon: 'home', iconType: 'fill', show: true},
|
||||
{name: '分类', path: '/categories', icon: 'project', iconType: 'fill', show: true},
|
||||
{name: '标签', path: '/tags', icon: 'tags', iconType: 'fill', show: true},
|
||||
// {name: '留言', path: '/leaveMsg', icon: 'carry-out', iconType: 'fill', show: true},
|
||||
{name: '更新', path: '/update', icon: 'up-square', iconType: 'fill', show: true},
|
||||
{name: '友链', path: '/links', icon: 'link', iconType: 'outline', show: true},
|
||||
{name: '登录', path: '/login', icon: 'login', iconType: 'outline', show: false},
|
||||
{name: '注册', path: '/registration', icon: 'user', iconType: 'outline', show: false}
|
||||
];
|
||||
|
||||
|
||||
this.showList = window.innerWidth > this.mobileMaxWidth;
|
||||
this.changeLoginButtonV();
|
||||
// 监听宽度变化
|
||||
windowWidthChange(() => {
|
||||
this.showList = window.innerWidth > this.mobileMaxWidth;
|
||||
this.changeLoginButtonV();
|
||||
});
|
||||
|
||||
this.router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe((e: RouterEvent) => {
|
||||
// indexOf ==> -1/index
|
||||
const indexOfParam = e.url.indexOf('?');
|
||||
const path = e.url.substr(0, indexOfParam === -1 ? e.url.length : indexOfParam);
|
||||
// lastIndexOf ==> 0/index
|
||||
const indexOf = path.lastIndexOf('/');
|
||||
const prefix = path.substr(0, indexOf === 0 ? path.length : indexOf);
|
||||
this.currentPath = prefix;
|
||||
if (prefix === '/user' || prefix === '/write' || prefix === '/update') {
|
||||
this.size = 'default';
|
||||
} else {
|
||||
this.size = 'large';
|
||||
}
|
||||
this.getInfo();
|
||||
});
|
||||
}
|
||||
|
||||
@Output() loginEvent = new EventEmitter();
|
||||
@Output() registrationEvent = new EventEmitter();
|
||||
size: 'large' | 'default';
|
||||
currentPath: string;
|
||||
|
||||
|
||||
public pageList: {
|
||||
path: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
iconType: 'outline' | 'fill' | 'twotone';
|
||||
show: boolean;
|
||||
}[];
|
||||
|
||||
|
||||
public showList = true;
|
||||
// css 样式中设置移动端最大宽度为910px 见src/app/global-variables.less
|
||||
private readonly mobileMaxWidth = 940;
|
||||
|
||||
@Input() userInfo: User;
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
changeMenuStatus() {
|
||||
this.showList = !this.showList;
|
||||
this.changeLoginButtonV();
|
||||
}
|
||||
|
||||
private changeLoginButtonV() {
|
||||
this.pageList.forEach(e => {
|
||||
if (e.name === '登录' || e.name === '注册') {
|
||||
if (this.userInfo) {
|
||||
e.show = false;
|
||||
} else {
|
||||
e.show = (this.showList && window.innerWidth < this.mobileMaxWidth);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dealLink(path: string) {
|
||||
this.showList = window.innerWidth > this.mobileMaxWidth;
|
||||
if (path === '/login') {
|
||||
this.login();
|
||||
} else if (path === '/registration') {
|
||||
this.registration();
|
||||
} else {
|
||||
this.router.navigateByUrl(path);
|
||||
}
|
||||
}
|
||||
|
||||
login() {
|
||||
this.showList = window.innerWidth > this.mobileMaxWidth;
|
||||
if (this.currentPath === '/article' || this.currentPath === '/write') {
|
||||
this.loginEvent.emit();
|
||||
return;
|
||||
}
|
||||
this.router.navigateByUrl('/user/login');
|
||||
}
|
||||
|
||||
registration() {
|
||||
this.showList = window.innerWidth > this.mobileMaxWidth;
|
||||
if (this.currentPath === '/article' || this.currentPath === '/write') {
|
||||
this.registrationEvent.emit();
|
||||
return;
|
||||
}
|
||||
this.router.navigateByUrl('/user/registration');
|
||||
}
|
||||
|
||||
getInfo() {
|
||||
this.apiService.userInfo().subscribe(data => {
|
||||
this.userInfo = data.result;
|
||||
this.changeLoginButtonV();
|
||||
},
|
||||
error => {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.apiService.logout().subscribe(data => {
|
||||
location.reload();
|
||||
},
|
||||
error => {
|
||||
}
|
||||
);
|
||||
this.userInfo = null;
|
||||
}
|
||||
|
||||
toAdminPage() {
|
||||
window.location.href = '/admin';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
index/src/app/global-variables.less
Normal file
13
index/src/app/global-variables.less
Normal file
@@ -0,0 +1,13 @@
|
||||
// 定义less 中的全局变量
|
||||
// 使用的时候导入本文件
|
||||
|
||||
/** 移动端适配时的分割宽度 */
|
||||
@max-width: 940px;
|
||||
|
||||
|
||||
/**** header ****/
|
||||
// header的最小高度
|
||||
@header-min-height: 300px;
|
||||
@header-logo-width: 50px;
|
||||
@header-logo-height: 50px;
|
||||
/**** header ****/
|
||||
@@ -1,330 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
<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>
|
||||
@@ -1,228 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
#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%;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<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>
|
||||
@@ -1,71 +0,0 @@
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#main{
|
||||
width: 60%;
|
||||
height: 50px;
|
||||
margin: 0 auto;
|
||||
padding: 15% 0;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<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>
|
||||
@@ -1,76 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
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%;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
<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>
|
||||
@@ -1,107 +0,0 @@
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
.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;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<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>
|
||||
@@ -1,94 +0,0 @@
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
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() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<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>
|
||||
@@ -1,65 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
.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%;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
<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>
|
||||
@@ -1,92 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
#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%;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<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>
|
||||
@@ -1,62 +0,0 @@
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
@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;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
<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>
|
||||
@@ -1,28 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
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());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/* @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;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<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>
|
||||
@@ -1,183 +0,0 @@
|
||||
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('文章不存在');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ArticleService } from './article.service';
|
||||
|
||||
describe('ArticleService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: ArticleService = TestBed.get(ArticleService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,97 +0,0 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpService} from '../http.service';
|
||||
import {Page} from '../../class/page';
|
||||
import {Article} from '../../class/article';
|
||||
import {Observable} from 'rxjs';
|
||||
import {Data} from '../../class/data';
|
||||
import {ArticleReq} from '../../class/articleReq';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ArticleService {
|
||||
|
||||
|
||||
constructor(public http: HttpService) {
|
||||
}
|
||||
|
||||
// 存储所有已经请求过的数据<首页数据>
|
||||
pageList: Page<Article>[] = [];
|
||||
|
||||
// 最后一次请求后的数据集<首页数据>
|
||||
currentPage: Page<Article>;
|
||||
|
||||
// 通过分类获取的article
|
||||
public currentArticleOfCategory: Page<Article>;
|
||||
// 通过分类获取的article
|
||||
public currentArticleOfTag: Page<Article>;
|
||||
|
||||
|
||||
/**
|
||||
* 获取文章
|
||||
* @param pageNum 页码数
|
||||
* @param pageSize 单页数据量
|
||||
*/
|
||||
getArticle(pageNum: number, pageSize: number): object {
|
||||
const articlePage = this.exist(pageNum, pageSize);
|
||||
if (articlePage) {
|
||||
return articlePage;
|
||||
}
|
||||
const observable = this.http.get('/articles?page=' + pageNum + '&count=' + pageSize);
|
||||
observable.subscribe((data: any) => {
|
||||
if (data.code === 0) {
|
||||
this.currentPage = data.result;
|
||||
this.pageList.push(data.result);
|
||||
}
|
||||
});
|
||||
return observable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据分类获取文章
|
||||
* @param name 分类名
|
||||
*/
|
||||
getArticleByCategory(name: string) {
|
||||
return this.http.get('/articles/category/' + name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签获取文章
|
||||
* @param name 标签名
|
||||
*/
|
||||
getArticleByTag(name: string) {
|
||||
return this.http.get('/articles/tag/' + name);
|
||||
}
|
||||
|
||||
getArticleById(id: number, update: boolean = false) {
|
||||
return this.http.get('/article/articleID/' + id + '?update=' + update);
|
||||
}
|
||||
|
||||
|
||||
createArticle(article: ArticleReq) {
|
||||
return this.http.post('/admin/article/create', article, true);
|
||||
}
|
||||
|
||||
updateArticle(article: ArticleReq) {
|
||||
return this.http.put('/admin/article/update', article);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断并返回数据
|
||||
* @param pageNum 页码数
|
||||
* @param pageSize 单页数据量
|
||||
*/
|
||||
private exist(pageNum: number, pageSize: number): Page<Article> {
|
||||
if (this.currentPage == null) {
|
||||
return null;
|
||||
}
|
||||
// tslint:disable-next-line:prefer-for-of
|
||||
for (let i = 0; i < this.pageList.length; i++) {
|
||||
// tslint:disable-next-line:triple-equals
|
||||
if (this.pageList[i].pageNum == pageNum && this.pageList[i].pageSize == pageSize) {
|
||||
return this.pageList[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CategoryService } from './category.service';
|
||||
|
||||
describe('CategoryService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: CategoryService = TestBed.get(CategoryService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,27 +0,0 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpService} from '../http.service';
|
||||
import {Category} from '../../class/category';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CategoryService {
|
||||
|
||||
constructor(public http: HttpService) {
|
||||
}
|
||||
|
||||
categories: Category[];
|
||||
|
||||
getAllCategory() {
|
||||
const observable = this.http.get('/categories');
|
||||
observable.subscribe((data) => {
|
||||
if (data.code === 0) {
|
||||
this.categories = data.result;
|
||||
}
|
||||
}
|
||||
);
|
||||
return observable;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CommentService } from './comment.service';
|
||||
|
||||
describe('CommentService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: CommentService = TestBed.get(CommentService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,124 +0,0 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpService} from '../http.service';
|
||||
import {Page} from '../../class/page';
|
||||
import {LeaveMsg} from '../../class/LeaveMsg';
|
||||
import {CommentReq} from '../../class/commentReq';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CommentService {
|
||||
|
||||
constructor(public http: HttpService) {
|
||||
}
|
||||
|
||||
// 存放
|
||||
leaveMsgPage: Page<LeaveMsg> = new Page();
|
||||
|
||||
commentPage: Page<LeaveMsg> = new Page();
|
||||
|
||||
|
||||
/**
|
||||
* 获取留言
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 单页数据数量
|
||||
*/
|
||||
getLeaveMsg(pageNum: number, pageSize: number) {
|
||||
const observable = this.http.get('/leaveMsg?count=' + pageSize + '&page=' + pageNum);
|
||||
observable.subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.leaveMsgPage = data.result;
|
||||
this.getResponseLeaveMsg();
|
||||
}
|
||||
});
|
||||
return observable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文章的评论
|
||||
* @param articleId 文章id
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 单页数量
|
||||
*/
|
||||
getPageComment(articleId: number, pageNum: number, pageSize: number) {
|
||||
const observable = this.http.get('/comments?articleId=' + articleId + '&count=' + pageSize + '&page=' + pageNum);
|
||||
observable.subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.commentPage = data.result;
|
||||
this.getResponseComment();
|
||||
}
|
||||
});
|
||||
return observable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取留言的回复
|
||||
*/
|
||||
getResponseLeaveMsg() {
|
||||
if (!this.leaveMsgPage.list) {
|
||||
return;
|
||||
}
|
||||
this.leaveMsgPage.list.forEach(leaveMsg => {
|
||||
if (leaveMsg.responseId != null && leaveMsg.responseId !== '') {
|
||||
this.getByPid(leaveMsg.id).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
leaveMsg.child = data.result.list;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取评论的回复
|
||||
*/
|
||||
getResponseComment() {
|
||||
if (!this.commentPage.list) {
|
||||
return;
|
||||
}
|
||||
this.commentPage.list.forEach(comment => {
|
||||
if (comment.responseId != null && comment.responseId !== '') {
|
||||
this.getByPid(comment.id).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
comment.child = data.result.list;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过父评论 获取回复
|
||||
* @param pid 父评论id
|
||||
*/
|
||||
getByPid(pid: number) {
|
||||
return this.http.get('/comment/pid/' + pid + '?count=5&page=1');
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交评论/留言 并加入缓存数据中
|
||||
* @param submitBody 请求体
|
||||
*/
|
||||
submitComment(submitBody: CommentReq) {
|
||||
this.http.post('/user/comment/create', submitBody, true).subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
if (!submitBody.comment) {
|
||||
this.leaveMsgPage.list.unshift(data.result);
|
||||
} else {
|
||||
this.commentPage.list.unshift(data.result);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 回复评论/留言
|
||||
* @param responseComment 请求体
|
||||
*/
|
||||
rely(responseComment: CommentReq) {
|
||||
return this.http.post('/user/comment/create', responseComment, true);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpService} from '../http.service';
|
||||
import {Count} from '../../class/count';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CountService {
|
||||
|
||||
constructor(public http: HttpService) {
|
||||
}
|
||||
|
||||
count: Count;
|
||||
|
||||
getCount() {
|
||||
const observable = this.http.get('/counts');
|
||||
observable.subscribe((data: any) => {
|
||||
if (data.code === 0) {
|
||||
this.count = data.result;
|
||||
}
|
||||
});
|
||||
return observable;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HttpService } from './http.service';
|
||||
|
||||
describe('HttpService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: HttpService = TestBed.get(HttpService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,110 +0,0 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpClient, HttpHeaders} from '@angular/common/http';
|
||||
import {environment} from '../../environments/environment';
|
||||
import {Observable} from 'rxjs';
|
||||
import {Data} from '../class/data';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class HttpService {
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
this.host = environment.host;
|
||||
const item = localStorage.getItem('token');
|
||||
this.token = item == null ? '' : item;
|
||||
this.httpOptions = {
|
||||
headers: new HttpHeaders({
|
||||
Accept: '*/*',
|
||||
Authorization: this.token
|
||||
}),
|
||||
withCredentials: true
|
||||
};
|
||||
}
|
||||
|
||||
// 请求的主机地址
|
||||
public host: string;
|
||||
private token: string;
|
||||
|
||||
/**
|
||||
* http请求配置
|
||||
*/
|
||||
private httpOptions: object;
|
||||
|
||||
/**
|
||||
* get 请求
|
||||
* @param path 路径
|
||||
*/
|
||||
get(path: string): Observable<Data> {
|
||||
return this.http.get<Data>(this.getPath(path), this.httpOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* post请求
|
||||
* @param path 路径
|
||||
* @param reqBody 请求体
|
||||
* @param isJson 请求数据是否是json格式
|
||||
*/
|
||||
post(path: string, reqBody: object, isJson: boolean): Observable<Data> {
|
||||
const Options = {
|
||||
headers: new HttpHeaders({
|
||||
Accept: '*/*',
|
||||
Authorization: this.token,
|
||||
ContentType: isJson ? 'application/json' : 'application/x-www-form-urlencoded'
|
||||
}),
|
||||
withCredentials: true
|
||||
};
|
||||
let submitBody = '';
|
||||
if (!isJson) {
|
||||
for (const key in reqBody) {
|
||||
// 跳过值为null的参数请求
|
||||
if (reqBody[key] == null || reqBody[key] === 'null') {
|
||||
continue;
|
||||
}
|
||||
submitBody = submitBody + '&' + key + '=' + reqBody[key];
|
||||
}
|
||||
submitBody = submitBody.substring(1);
|
||||
}
|
||||
return this.http.post<Data>(this.getPath(path), isJson ? reqBody : submitBody, Options);
|
||||
}
|
||||
|
||||
/**
|
||||
* put 请求
|
||||
* @param path 请求路径
|
||||
* @param reqBody 请求体
|
||||
*/
|
||||
put(path: string, reqBody: object): Observable<Data> {
|
||||
return this.http.put<Data>(this.getPath(path), reqBody, this.httpOptions);
|
||||
}
|
||||
|
||||
visit() {
|
||||
this.post('/visit', null, true).subscribe(data => {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查path 并拼接
|
||||
* @param path 请求路径
|
||||
* @return 拼接后的url
|
||||
*/
|
||||
private getPath(path: string): string {
|
||||
if (path == null || path.length === 0 || path.substr(0, 1) !== '/') {
|
||||
throw new Error('路径不合法');
|
||||
}
|
||||
return this.host + path;
|
||||
}
|
||||
|
||||
setToken(t: string) {
|
||||
if (t == null) {
|
||||
return;
|
||||
}
|
||||
localStorage.setItem('token', t);
|
||||
this.token = t;
|
||||
}
|
||||
|
||||
removeToken() {
|
||||
localStorage.removeItem('token');
|
||||
this.token = ''
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LinkService } from './link.service';
|
||||
|
||||
describe('LinkService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: LinkService = TestBed.get(LinkService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpService} from '../http.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LinkService {
|
||||
|
||||
constructor(public http: HttpService) {
|
||||
}
|
||||
|
||||
public Links: { id: number, name: string, url: string };
|
||||
|
||||
apply(link: {
|
||||
name: string,
|
||||
url: string
|
||||
}) {
|
||||
return this.http.post('/apply', link, false);
|
||||
}
|
||||
|
||||
getLinks() {
|
||||
this.http.get('/links').subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.Links = data.result;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TagService } from './tag.service';
|
||||
|
||||
describe('TagService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: TagService = TestBed.get(TagService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpService} from '../http.service';
|
||||
import {Tag} from '../../class/tag';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TagService {
|
||||
|
||||
constructor(public http: HttpService) {
|
||||
}
|
||||
|
||||
tagCloudList: Tag[];
|
||||
|
||||
getTagCloud() {
|
||||
const observable = this.http.get('/tags/nac');
|
||||
observable.subscribe((data: any) => {
|
||||
if (data.code === 0) {
|
||||
this.tagCloudList = data.result;
|
||||
}
|
||||
});
|
||||
return observable;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { WebUpdateService } from './web-update.service';
|
||||
|
||||
describe('WebUpdateService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: WebUpdateService = TestBed.get(WebUpdateService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpService} from '../http.service';
|
||||
import {UpdateInfo} from '../../class/updateInfo';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class WebUpdateService {
|
||||
|
||||
constructor(public http: HttpService) {
|
||||
}
|
||||
|
||||
public updateInfoList: UpdateInfo[];
|
||||
|
||||
public lastestUpdateTime: string;
|
||||
|
||||
// when you fell unhappy,look at the sky, the sun is shining the birds are singing
|
||||
// And you? should be smiling
|
||||
getUpdateInfo() {
|
||||
this.http.get('/webUpdate').subscribe((data: any) => {
|
||||
if (data.code === 0) {
|
||||
this.updateInfoList = data.result.reverse();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getLastestUpdateTime() {
|
||||
this.http.get('/lastestUpdateTime').subscribe(data => {
|
||||
if (data.code === 0) {
|
||||
this.lastestUpdateTime = data.result;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserService } from './user.service';
|
||||
|
||||
describe('UserService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: UserService = TestBed.get(UserService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,100 +0,0 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpService} from '../http.service';
|
||||
import {User} from '../../class/user';
|
||||
import {LoginReq} from '../../class/loginReq';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserService {
|
||||
|
||||
constructor(public http: HttpService) {
|
||||
}
|
||||
|
||||
userInfo: User;
|
||||
|
||||
// 刚注册完账户 实现自动填充账户的临时存储 登录成功即置空
|
||||
tempUser: LoginReq;
|
||||
|
||||
loginModalType: 'login' | 'registration' = 'login';
|
||||
loginModalVisible: boolean = false;
|
||||
|
||||
|
||||
showModal(type: 'login' | 'registration') {
|
||||
this.loginModalType = type;
|
||||
this.loginModalVisible = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
getUserInfo() {
|
||||
const observable = this.http.get('/user/userInfo');
|
||||
observable.subscribe((data: any) => {
|
||||
if (data.code === 0) {
|
||||
this.userInfo = data.result;
|
||||
}
|
||||
});
|
||||
return observable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销登录
|
||||
*/
|
||||
logout() {
|
||||
this.http.get('/logout').subscribe((data: any) => {
|
||||
if (data.code === 0) {
|
||||
this.userInfo = null;
|
||||
this.http.removeToken();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* @param loginReq 请求体
|
||||
*/
|
||||
login(loginReq: {
|
||||
'email': string,
|
||||
'isRememberMe': boolean,
|
||||
'password': string
|
||||
}) {
|
||||
const observable = this.http.post('/login', loginReq, true);
|
||||
observable.subscribe((data: any) => {
|
||||
if (data.code === 0) {
|
||||
this.userInfo = data.result;
|
||||
this.loginModalVisible = false;
|
||||
this.http.setToken(data.result.token);
|
||||
}
|
||||
});
|
||||
return observable;
|
||||
}
|
||||
|
||||
registration(emailStr: string, pwd: string) {
|
||||
const submitBody = {
|
||||
email: emailStr,
|
||||
password: pwd
|
||||
};
|
||||
return this.http.post('/registration', submitBody, false);
|
||||
// 注册成功 -> 登录 在component里面实现了
|
||||
}
|
||||
|
||||
|
||||
emailVerify(reqBody) {
|
||||
return this.http.post('/emailVerify', reqBody, false);
|
||||
}
|
||||
|
||||
resetPWd(reqBody) {
|
||||
return this.http.post('/resetPwd', reqBody, false);
|
||||
}
|
||||
|
||||
sendResetPwdEmail(emailStr: string) {
|
||||
return this.http.post('/sendResetPwdEmail', {email: emailStr}, false);
|
||||
}
|
||||
|
||||
imgCodeVerify(codeStr: string) {
|
||||
return this.http.post('/verCode', {code: codeStr}, false);
|
||||
}
|
||||
|
||||
}
|
||||
9
index/src/app/utils/color.ts
Normal file
9
index/src/app/utils/color.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export const ColorList: { bgColor: string, fontColor: string }[] = [
|
||||
{bgColor: '#7bcfa6', fontColor: '#000000'}, // 石青
|
||||
{bgColor: '#bce672', fontColor: '#000000'}, // 松花色
|
||||
{bgColor: '#ff8936', fontColor: '#000000'}, // 橘黄
|
||||
{bgColor: '#f0c239', fontColor: '#000000'}, // 缃色
|
||||
{bgColor: '#808080', fontColor: '#ffffff'}, // 灰色
|
||||
{bgColor: '#3eede7', fontColor: '#000000'}, // 碧蓝
|
||||
{bgColor: '#177cb0', fontColor: '#ffffff'}, // 靛青
|
||||
];
|
||||
@@ -1,12 +1,12 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CountService } from './count.service';
|
||||
|
||||
describe('CountService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: CountService = TestBed.get(CountService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LocalStorageService } from './local-storage.service';
|
||||
|
||||
describe('LocalStorageService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: LocalStorageService = TestBed.get(LocalStorageService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
63
index/src/app/utils/local-storage.service.ts
Normal file
63
index/src/app/utils/local-storage.service.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {User} from '../class/User';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LocalStorageService {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
// 1分钟
|
||||
readonly place = 60 * 1000;
|
||||
|
||||
getToken(): string {
|
||||
return localStorage.getItem('token');
|
||||
}
|
||||
|
||||
setToken(token: string) {
|
||||
localStorage.setItem('t', new Date().valueOf().toString());
|
||||
localStorage.setItem('token', token);
|
||||
}
|
||||
|
||||
removeToken() {
|
||||
localStorage.removeItem('token');
|
||||
}
|
||||
|
||||
isLogin() {
|
||||
return this.getToken() != null;
|
||||
}
|
||||
|
||||
setUser(user: User) {
|
||||
// TODO: 简单加个密
|
||||
localStorage.setItem('t', new Date().valueOf().toString());
|
||||
return localStorage.setItem('user', JSON.stringify(user));
|
||||
}
|
||||
|
||||
getUser(): User {
|
||||
if (!this.checkNeedNet()) {
|
||||
return JSON.parse(localStorage.getItem('user'));
|
||||
}
|
||||
}
|
||||
|
||||
removeUser() {
|
||||
return localStorage.removeItem('user');
|
||||
}
|
||||
|
||||
clear() {
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('user');
|
||||
return localStorage.removeItem('t');
|
||||
}
|
||||
|
||||
checkNeedNet() {
|
||||
const t: number = Number.parseInt(localStorage.getItem('t'), 10);
|
||||
if (isNaN(t) || new Date().valueOf() - t > this.place) {
|
||||
localStorage.removeItem('t');
|
||||
localStorage.removeItem('user');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
42
index/src/app/utils/logger.ts
Normal file
42
index/src/app/utils/logger.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import {environment} from '../../environments/environment';
|
||||
|
||||
export class Logger {
|
||||
|
||||
private static inited = false;
|
||||
|
||||
static info(obj: object) {
|
||||
this.printLogInfo();
|
||||
if (environment.logger) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info(obj);
|
||||
}
|
||||
}
|
||||
|
||||
static debug(obj: object) {
|
||||
this.printLogInfo();
|
||||
if (environment.logger) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.debug(obj);
|
||||
}
|
||||
}
|
||||
|
||||
static error(obj: object) {
|
||||
this.printLogInfo();
|
||||
if (environment.logger) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.error(obj);
|
||||
}
|
||||
}
|
||||
|
||||
static printLogInfo() {
|
||||
const option = {
|
||||
environment: environment.production ? 'release' : 'debug',
|
||||
};
|
||||
if (!this.inited) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info(option);
|
||||
this.inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
index/src/app/utils/svgIconUtil.ts
Normal file
11
index/src/app/utils/svgIconUtil.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export class SvgIconUtil {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:max-line-length
|
||||
static readonly nameIcon = '<svg t="1581933298087" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3223" width="200" height="200"><path d="M983.04 163.84H40.96c-24.576 0-40.96 16.384-40.96 40.96v614.4c0 20.48 16.384 40.96 40.96 40.96h942.08c20.48 0 40.96-20.48 40.96-40.96V204.8c0-24.576-20.48-40.96-40.96-40.96zM253.952 749.568c0-102.4 61.44-192.512 147.456-233.472-28.672-24.576-45.056-61.44-45.056-102.4 0-77.824 65.536-143.36 143.36-143.36s143.36 65.536 143.36 143.36c0 40.96-12.288 73.728-36.864 98.304 94.208 36.864 163.84 131.072 163.84 237.568H253.952z" p-id="3224" fill="#1296db"></path></svg>';
|
||||
|
||||
// tslint:disable-next-line:max-line-length
|
||||
static readonly locationIcon = '<svg t="1581933583276" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3981" width="200" height="200"><path d="M511.932268 0c-213.943865 0-387.880498 170.933833-387.880499 381.06211 0 164.815346 238.869364 485.098975 341.66447 620.247558a58.249807 58.249807 0 0 0 46.216029 22.690332c18.129688 0 35.491743-8.443964 46.328916-22.690332 102.704796-135.126006 341.709624-455.432213 341.709624-620.247558C899.970808 170.933833 725.89871 0 511.932268 0z m0 519.574733c-91.393496 0-165.786176-72.902569-165.786176-162.670489 0-89.722765 74.39268-162.738221 165.786176-162.73822 91.438651 0 165.718443 73.015456 165.718443 162.73822 0 89.76792-74.279793 162.670488-165.718443 162.670489z" fill="#1296db" p-id="3982"></path></svg>';
|
||||
}
|
||||
17
index/src/app/utils/util.ts
Normal file
17
index/src/app/utils/util.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
class DataProperties {
|
||||
static windowsMode: 'small' | 'middle' | 'large' = null;
|
||||
}
|
||||
|
||||
export function windowWidthChange(func) {
|
||||
DataProperties.windowsMode = window.innerWidth < 910 ? 'small' : (window.innerWidth > 1300 ? 'large' : 'middle');
|
||||
// 监听宽度变化
|
||||
window.addEventListener('resize', () => {
|
||||
DataProperties.windowsMode = window.innerWidth < 910 ? 'small' : (window.innerWidth > 1300 ? 'large' : 'middle');
|
||||
func();
|
||||
});
|
||||
}
|
||||
|
||||
export function windowsMode() {
|
||||
return DataProperties.windowsMode;
|
||||
}
|
||||
|
||||
18
index/src/app/view/article/article-routing.module.ts
Normal file
18
index/src/app/view/article/article-routing.module.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {ArticleComponent} from './article.component';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '**', component: ArticleComponent}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes)
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
|
||||
export class ArticleRoutingModule {
|
||||
}
|
||||
109
index/src/app/view/article/article.component.html
Normal file
109
index/src/app/view/article/article.component.html
Normal file
@@ -0,0 +1,109 @@
|
||||
<div id="main">
|
||||
|
||||
<div id="article-content"></div>
|
||||
<ng-template [ngIf]="article">
|
||||
<span id="over">over</span>
|
||||
|
||||
<!-- 文章版权 -->
|
||||
<div id="copyright">
|
||||
<p>本文作者:{{article.authorName}} </p>
|
||||
<p>{{article.original ? "本文" : "原文"}}链接:{{article.original ? copyRightUrl : article.url}}</p>
|
||||
<p>版权声明:转载请注明出处</p>
|
||||
</div>
|
||||
|
||||
<nz-divider></nz-divider>
|
||||
<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]="['/tags']" [queryParams]="{name:item}">{{item}}</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="article-bAnda">
|
||||
<a (click)="toArticle(article.nextArticleId)" [class.disabled]="article.nextArticleId==-1">
|
||||
<i nz-icon nzType="left" nzTheme="outline"></i> {{article.nextArticleTitle}}
|
||||
</a>
|
||||
<a (click)="toArticle(article.preArticleId)" [class.disabled]="article.preArticleId==-1"
|
||||
style="float: right" id="pre">
|
||||
{{article.preArticleTitle}} <i nz-icon nzType="right" nzTheme="outline"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- TODO:::评论列表 -->
|
||||
</ng-template>
|
||||
|
||||
<nz-comment id="f-comment">
|
||||
<nz-avatar nz-comment-avatar nzIcon="user" [nzSrc]="avatarImgUrl"></nz-avatar>
|
||||
<nz-comment-content>
|
||||
<nz-form-item>
|
||||
<textarea [(ngModel)]="comment.content" nz-input rows="4" [disabled]="!user"
|
||||
[nzAutosize]="{ minRows: 2, maxRows: user?6:2 }"></textarea>
|
||||
<span *ngIf="!user">请先 <a routerLink="/user/login"
|
||||
[queryParams]="{url:'/article/'+articleId}">登录</a></span>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<button nz-button nzType="primary" [nzLoading]="!user&&submitting"
|
||||
[disabled]="!comment.content"
|
||||
(click)="submitComment(true)">评论
|
||||
</button>
|
||||
</nz-form-item>
|
||||
</nz-comment-content>
|
||||
</nz-comment>
|
||||
|
||||
<div class="tab">评论</div>
|
||||
<div class="tab-bottom"></div>
|
||||
<div *ngIf="commentPage&&!commentPage.list.length" class="no-comment-tip">暂无评论,赶快去抢个沙发吧</div>
|
||||
<ng-template [ngIf]="commentPage">
|
||||
|
||||
<nz-list [nzLoading]="!commentPage">
|
||||
<nz-list-item *ngFor="let comment of commentPage.list">
|
||||
<nz-comment [nzAuthor]="comment.authorName" [nzDatetime]="comment.date" style="width: 100%">
|
||||
<nz-avatar nz-comment-avatar nzIcon="user" [nzSrc]="comment.authorAvatarImgUrl"></nz-avatar>
|
||||
<nz-comment-content>
|
||||
<p style="font-size: larger">{{ comment.content }}</p>
|
||||
</nz-comment-content>
|
||||
<nz-comment-action>
|
||||
<i nz-icon nzType="message" nzTheme="outline"></i>
|
||||
<button nz-button nzType="link" (click)="resp(comment.id,comment.authorName)">回复</button>
|
||||
</nz-comment-action>
|
||||
|
||||
<nz-list-item *ngFor="let com of comment.respComment">
|
||||
<nz-comment [nzAuthor]="com.authorName" [nzDatetime]="com.date">
|
||||
<nz-avatar nz-comment-avatar nzIcon="user" [nzSrc]="com.authorAvatarImgUrl"></nz-avatar>
|
||||
<nz-comment-content>
|
||||
<p style="font-size: larger">{{ com.content }}</p>
|
||||
</nz-comment-content>
|
||||
<!--<nz-comment-action>
|
||||
<i nz-icon nzType="message" nzTheme="outline"></i>
|
||||
<button nz-button nzType="link" (click)="resp(comment.id,com.authorName)">回复</button>
|
||||
</nz-comment-action>-->
|
||||
</nz-comment>
|
||||
</nz-list-item>
|
||||
|
||||
<nz-form-item *ngIf="pid==comment.id">
|
||||
<nz-input-group [nzAddOnBefore]="respName">
|
||||
<textarea nz-input [(ngModel)]="content" placeholder="写出你的想法"
|
||||
[nzAutosize]="{ minRows: 2, maxRows: 6 }" [disabled]="!user">
|
||||
</textarea>
|
||||
<ng-template #respName>
|
||||
<span>@{{name}}</span>
|
||||
</ng-template>
|
||||
</nz-input-group>
|
||||
<div *ngIf="!user">请先 <a routerLink="/user/login"
|
||||
[queryParams]="{url:'/article/'+articleId}">登录</a></div>
|
||||
<button nz-button (click)="pid=null" style="margin-top: 10px;">取消</button>
|
||||
<button nz-button nzType="primary" (click)="submitComment(false)"
|
||||
style="margin-left: 30px;margin-top: 10px;" [nzLoading]="!user&&submitting">回复
|
||||
</button>
|
||||
|
||||
</nz-form-item>
|
||||
</nz-comment>
|
||||
|
||||
</nz-list-item>
|
||||
</nz-list>
|
||||
</ng-template>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user