Compare commits

...

164 Commits
v1.0 ... v2.1.0

Author SHA1 Message Date
禾几海
bf2b0f0c64 Update buildTest.yml 2020-07-29 14:37:45 +08:00
禾几海
bc4cce8ec3 Update and rename nodejs.yml to build.yml 2020-07-29 14:37:17 +08:00
禾几海
4479d1536f 关闭双击编辑 2020-07-29 14:34:30 +08:00
禾几海
fa2eed47ab 修复bug
修复bug
2020-07-26 14:06:53 +08:00
禾几海
414497ac70 开发环境 2020-07-26 13:43:24 +08:00
禾几海
6fce3f646b add api 2020-07-26 13:43:05 +08:00
禾几海
f42beeb5bb fix bug 2020-07-26 13:42:30 +08:00
禾几海
2008f43522 表格title 2020-07-23 15:08:58 +08:00
禾几海
c7e364f6bd Merge pull request #18 from xiaohai2271/dev
公共组件
2020-07-11 10:41:49 +08:00
禾几海
38e24fdd2c 更新依赖 2020-07-11 10:33:52 +08:00
禾几海
0b810a8ef5 . 2020-07-11 10:14:56 +08:00
禾几海
f87b8041e8 . 2020-07-11 10:12:05 +08:00
禾几海
d5d592fee5 访客管理 2020-07-11 10:11:48 +08:00
禾几海
89ec668b39 用户管理 2020-07-11 10:03:22 +08:00
禾几海
36db5289a4 更新内容管理 2020-07-11 09:44:32 +08:00
禾几海
cc8853e081 公共表格组件-ng-content 2020-07-11 09:39:01 +08:00
禾几海
30a2fbdecc 标签管理 2020-07-11 00:48:15 +08:00
禾几海
54b3b643c9 公共表格组件-数据加载 2020-07-11 00:47:27 +08:00
禾几海
5524b8a80b 公共可编辑Tag组件-新增api 2020-07-11 00:36:19 +08:00
禾几海
eb4b9ea440 公共表格组件-title传null隐藏头部显示 2020-07-10 23:15:05 +08:00
禾几海
8a8302eb51 友链管理 2020-07-03 17:30:10 +08:00
禾几海
06d1d2e48c 评论管理2/2 2020-07-03 13:42:10 +08:00
禾几海
3a4e0ec815 数据变更提示 2020-07-03 13:11:46 +08:00
禾几海
d800fa479b 调整 2020-07-03 12:59:21 +08:00
禾几海
efa1f0bee2 通用组件-表格上下文增加data数据,可编辑组件增加异步编辑 2020-07-03 12:59:20 +08:00
禾几海
c8fe9c3658 调整 2020-07-02 23:16:34 +08:00
禾几海
8bd8986bdd 通用表格组件-更改getValue函数实现逻辑,更改Data字段名 2020-07-02 23:16:10 +08:00
禾几海
551dbc10e5 通用可编辑组件-显示文本,数据提交回调 2020-07-02 23:09:39 +08:00
禾几海
5814a484c6 通用表格组件-request数据变更重新请求数据 2020-07-02 22:05:30 +08:00
禾几海
94ba2c2281 selector名 2020-07-02 21:51:15 +08:00
禾几海
03018d402f 通用表格组件-selector名 2020-07-02 21:51:04 +08:00
禾几海
d9a7b68a2b 通用可编辑tag组件-边框,编辑按钮 2020-07-02 21:48:29 +08:00
禾几海
1e3472244e 通用可编辑tag组件-测试入口 2020-07-02 13:15:50 +08:00
禾几海
e1efc88103 通用可编辑tag组件 2020-07-02 13:15:37 +08:00
禾几海
7da74fcba1 评论切换通用组件1/2 2020-07-01 23:02:38 +08:00
禾几海
c96b4d90da 通用组件-eval异常 2020-07-01 22:44:42 +08:00
禾几海
3330d0a090 文章管理改用通用组件 2020-07-01 18:14:03 +08:00
禾几海
7ce46ab634 通用组件-表格滚动,微调 2020-07-01 18:08:56 +08:00
禾几海
9dca081c7a 通用组件-确认框 2020-07-01 17:58:26 +08:00
禾几海
1d46241a06 微调 2020-07-01 17:18:47 +08:00
禾几海
7be9dc3f8a 关闭公共组件测试入口 2020-07-01 14:30:14 +08:00
禾几海
550b95ef8e Merge branch 'feature1' into dev 2020-07-01 14:25:05 +08:00
禾几海
845752b432 通用组件-getValue函数的0值bug 2020-07-01 14:17:54 +08:00
禾几海
6bd15f3cc0 通用组件-添加分页信息改变通知事件 2020-07-01 14:11:17 +08:00
禾几海
8cc8f8e8d5 通用组件-TemplateRef应用 2020-07-01 13:54:32 +08:00
禾几海
014a9eebe4 通用组件-多级属性显示 2020-07-01 12:40:45 +08:00
禾几海
7a788dc559 通用组件-刷新 2020-07-01 12:08:47 +08:00
禾几海
5e2dfe056b 通用组件-分页 2020-07-01 00:18:49 +08:00
禾几海
c265ff68fc Request 路径的bug 2020-07-01 00:10:36 +08:00
禾几海
3dce668594 Request 路径的bug 2020-07-01 00:08:13 +08:00
禾几海
718a7516af 编写通用组件 2020-07-01 00:05:00 +08:00
禾几海
e3a4862e13 编写通用组件测试页面 2020-06-30 23:56:45 +08:00
禾几海
781513d8c9 编写通用组件 2020-06-30 23:56:16 +08:00
禾几海
c271b1a8cf 编写通用组件 2020-06-30 18:12:23 +08:00
禾几海
b0b7e5d1f8 Merge pull request #17 from xiaohai2271/dev
修复空值,显示异常bug
2020-06-30 17:44:08 +08:00
禾几海
b60688cfd7 修复空值,显示异常bug 2020-06-30 17:31:38 +08:00
禾几海
9cf543a0ab 调整接口
调整接口
2020-05-27 18:31:02 +08:00
禾几海
e66d3bbf62 增加刷新按钮 2020-05-27 16:41:32 +08:00
禾几海
dba21730ca 接口调整,对接后端部分的v2版本 2020-05-27 16:41:06 +08:00
禾几海
e51725bc21 Merge branch 'dev' 2020-05-25 21:54:18 +08:00
禾几海
deb8646bda update README.md 2020-05-25 21:53:38 +08:00
禾几海
dba14fe5eb Merge remote-tracking branch 'origin/master' 2020-05-19 19:57:38 +08:00
禾几海
ffe4b36f3e 添加密码修改功能,完善抽屉部分信息的展示,修改头像显示异常的错误 2020-05-19 19:52:54 +08:00
禾几海
f8d3494626 移除本地缓存用户信息 2020-05-19 17:44:04 +08:00
禾几海
7b6a63cd84 Merge branch 'dev' 2020-05-18 17:24:04 +08:00
禾几海
00f69509c3 修改样式 2020-05-18 17:04:27 +08:00
禾几海
f21438f968 icon 2020-05-18 10:35:05 +08:00
禾几海
728f8c4a03 Merge branch 'dev' 2020-05-18 09:25:34 +08:00
禾几海
8a0f5726da table横向滚动 2020-05-18 09:18:09 +08:00
禾几海
c0002d5310 调整为响应式页面 2020-05-18 09:00:01 +08:00
禾几海
cdc4f3ea0f 更新token 2020-05-18 08:28:30 +08:00
禾几海
e291a69e43 Merge pull request #15 from xiaohai2271/dev
添加Service Worker,添加测试ci
2020-05-17 00:27:20 +08:00
小海
d3a122065e . 2020-05-17 00:20:09 +08:00
禾几海
7987da4d4a Update buildTest.yml 2020-05-17 00:13:59 +08:00
小海
e3dc949245 添加测试构建的ci 2020-05-17 00:11:10 +08:00
小海
955f537d59 添加Service Worker 缓存网站 2020-05-17 00:07:20 +08:00
小海
565fc9f673 调整用户头像展示问题,title遗漏 2020-05-16 23:05:57 +08:00
小海
67c509a245 Merge pull request #14 from xiaohai2271/issue11
合并为一个项目
2020-05-16 22:35:08 +08:00
小海
3e5524397b 调整ci 2020-05-16 22:25:13 +08:00
小海
42177a7721 修改路径 2020-05-16 22:18:45 +08:00
小海
abc792a561 微调 2020-05-16 21:58:47 +08:00
小海
446d7e06e4 针对普通用户的界面 2020-05-16 21:55:35 +08:00
小海
1c7f189d11 修复异常 2020-05-16 21:18:29 +08:00
小海
b45f3f16e3 修复第一次路由不通过时卡在空白页面的异常 2020-05-16 21:12:27 +08:00
小海
dc17bba056 修改部分错误,抽屉中增加注销 2020-05-16 21:04:23 +08:00
小海
5371a9a5c5 . 2020-05-16 20:50:20 +08:00
小海
e61e59fa5f 仪表盘页面 2020-05-16 20:48:09 +08:00
小海
7faebe83d5 . 2020-05-16 19:53:23 +08:00
小海
ae62cbc58f 微调 2020-05-16 18:51:14 +08:00
小海
992f0919cf 访客信息管理 2020-05-16 18:49:12 +08:00
小海
adaa54b7ec 更新管理 2020-05-16 18:41:57 +08:00
小海
92e17f58ea ... 2020-05-16 18:09:15 +08:00
小海
4537f78015 用户管理 2020-05-16 18:08:48 +08:00
小海
bc55aa48ff ... 2020-05-16 16:25:48 +08:00
小海
05143da5e2 友链管理 2020-05-16 16:25:22 +08:00
小海
4571d52e5d 评论管理 2020-05-16 15:41:20 +08:00
小海
1930fe2f76 修改路由守卫 2020-05-16 14:47:38 +08:00
小海
61d9d8949c 修改同一请求限制时间, 2020-05-16 14:46:43 +08:00
小海
441b5c9d10 调整 2020-05-16 13:34:54 +08:00
小海
035966e5e7 移除无效模块 2020-05-16 02:01:50 +08:00
小海
bc512d17b0 标签分类管理页面 2020-05-16 01:59:38 +08:00
小海
ea8be85015 . 2020-05-16 00:35:23 +08:00
小海
dbc69956d4 文章管理 2020-05-16 00:32:10 +08:00
小海
6f540acebc delete test files 2020-05-15 21:01:10 +08:00
小海
dd6f85dc62 ... 2020-05-15 20:46:13 +08:00
小海
b266340ef1 完善路由守卫功能 2020-05-15 20:46:02 +08:00
小海
e54726b4e8 修改信息 2020-05-15 19:52:20 +08:00
小海
a5dcdea130 更名 2020-05-15 19:26:00 +08:00
小海
eb7b3f6022 更名,删除测试文件 2020-05-15 19:24:59 +08:00
小海
6a4775f503 . 2020-05-15 18:08:22 +08:00
小海
d7a0adacb2 懒加载 2020-05-15 18:02:45 +08:00
小海
c18c32de98 delete service files 2020-05-15 18:01:36 +08:00
小海
349431c1d8 delete test files 2020-05-15 18:00:15 +08:00
小海
52f51138cb delete test file 2020-05-05 21:30:19 +08:00
小海
ce251b1217 Copy files 2020-05-05 21:13:17 +08:00
小海
dd38eaac54 响应数据类型异常 2020-05-05 21:12:14 +08:00
小海
76a9f590b8 完善文章管理页面 2020-04-29 14:09:02 +08:00
小海
62e538ac3c Merge branch 'master' into issue11 2020-04-29 10:57:06 +08:00
小海
45efb7eff4 Revert "创作页面引入下拉列表错误 #7" 编辑框依赖于该框架
This reverts commit 612b626ca0.
2020-04-29 10:55:13 +08:00
小海
c40837b1b2 完善api列表 2020-04-26 08:11:43 +08:00
小海
b2709cffd8 . 2020-04-25 21:53:57 +08:00
小海
f74aca491b 管理页面的主体界面 2020-04-25 17:29:29 +08:00
小海
bd5d10c8b7 管理页面的header 2020-04-25 17:29:01 +08:00
小海
adb8f889c6 多播异常 2020-04-25 17:19:25 +08:00
小海
94e2c27f8b 请求重复,多次请求会广播err导致接收到null值 2020-04-25 17:09:25 +08:00
小海
d4f5b3536f Merge branch 'master' into issue11 2020-04-25 15:44:31 +08:00
小海
612b626ca0 创作页面引入下拉列表错误 #7 2020-04-25 15:41:29 +08:00
小海
e96e2c2f0d 修改登录,获取用户信息和注销的方法调用 2020-04-25 15:33:57 +08:00
小海
e397131473 将登录,获取用户信息和注销的数据处理移到service层 2020-04-25 15:32:32 +08:00
小海
94b7c7e7ba 移除无效文件 2020-04-24 14:05:48 +08:00
小海
35736ab3e1 添加路由守卫 2020-04-24 11:07:55 +08:00
小海
042c4d4fda 空指针 2020-04-24 10:28:14 +08:00
小海
757cdf31df formatting 2020-04-24 10:27:56 +08:00
小海
f36287fc43 Merge branch 'master' into issue11 2020-04-23 23:22:50 +08:00
小海
5d9c385d46 时序图的bug 2020-04-23 23:22:08 +08:00
小海
6f9eb718ce 将组件的状态交给服务来控制 2020-04-23 23:18:29 +08:00
小海
2bcca393e1 立项 2020-04-23 21:59:32 +08:00
小海
caad40170a update README.md 2020-04-23 19:41:10 +08:00
小海
9ed77d83dd update nodejs.yml 2020-04-23 19:38:50 +08:00
小海
2aa5d91857 update nodejs.yml 2020-04-23 19:37:21 +08:00
小海
d3e55152b0 修复上次提交疏漏的bug 2020-04-23 18:13:09 +08:00
小海
b333f554b9 Merge pull request #13 from xiaohai2271/update
展示最近的提交记录
2020-04-23 17:35:54 +08:00
小海
3cd3ca7afc 展示最近的提交记录 2020-04-23 17:29:47 +08:00
小海
1f4b447a74 编辑器上传文件跨域 #9 2020-04-22 15:20:08 +08:00
小海
d67f2ed7ca update README.md 2020-04-22 10:07:50 +08:00
小海
7aada7801f 添加ci状态badge 2020-04-22 10:03:01 +08:00
小海
3ca8d6a4ff 更新图片 2020-04-22 10:02:19 +08:00
小海
71669bacf3 editor.md的时序图中的raphael库引发的下拉列表错位 #7 2020-04-21 23:53:02 +08:00
小海
ca190ced8a 登录注册按钮和信息框错位的bug #7 2020-04-21 23:47:04 +08:00
小海
102908a864 切换到github ci 2020-04-21 21:57:17 +08:00
小海
0a9608b697 修复宽度适配失效 #8 2020-04-17 14:41:38 +08:00
小海
d54b508236 将service移动到services文件夹中 2020-04-16 18:44:51 +08:00
小海
6b84118d16 修改articles字段 2020-04-16 18:34:23 +08:00
小海
f85973f4c8 . 2020-04-16 18:15:41 +08:00
小海
c569f7a63f 调整class文件,合并部分class到一个文件中 2020-04-16 18:15:13 +08:00
小海
a30ee898ba 更新到最新依赖 2020-04-16 17:11:51 +08:00
小海
9b511e7fe5 Merge pull request #5 from xiaohai2271/dependabot/npm_and_yarn/admin/handlebars-4.7.6
Bump handlebars from 4.1.2 to 4.7.6 in /admin
2020-04-14 20:20:05 +08:00
dependabot[bot]
cb880e1ac1 Bump handlebars from 4.1.2 to 4.7.6 in /admin
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.1.2 to 4.7.6.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.1.2...v4.7.6)

Signed-off-by: dependabot[bot] <support@github.com>
2020-04-06 12:59:07 +00:00
小海
8d1169ccd0 Merge pull request #6 from xiaohai2271/dependabot/npm_and_yarn/admin/acorn-6.4.1
Bump acorn from 6.2.0 to 6.4.1 in /admin
2020-04-06 20:56:27 +08:00
dependabot[bot]
5cf4571e65 Bump acorn from 6.2.0 to 6.4.1 in /admin
Bumps [acorn](https://github.com/acornjs/acorn) from 6.2.0 to 6.4.1.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/6.2.0...6.4.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-04-06 12:55:49 +00:00
小海
3ea8f63abc 重构界面ui 2020-04-06 20:48:40 +08:00
小海
f2791ee150 修复bug 2019-12-15 22:20:05 +08:00
小海
df944335da Update .gitignore 2019-12-01 15:45:00 +08:00
小海
d8eaa0d471 自动部署 2019-12-01 15:44:25 +08:00
780 changed files with 101990 additions and 104020 deletions

41
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Build
on:
push:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: '12.x'
- run: npm install -g @angular/cli
- run: bash build.sh
- name: SCP
uses: appleboy/scp-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
password: ${{ secrets.SSH_PASSWORD }}
port: ${{ secrets.SSH_PORT }}
source: "index.tar"
target: "/www/wwwroot/celess.cn"
- name: Run SSH command
uses: garygrossgarten/github-action-ssh@v0.5.0
with:
command: cd /www/wwwroot/celess.cn && bash deploy.sh
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
password: ${{ secrets.SSH_PASSWORD }}
port: ${{ secrets.SSH_PORT }}

20
.github/workflows/buildTest.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: test
on:
push:
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: '12.x'
- run: npm install -g @angular/cli
- run: bash build.sh

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
dist
node_modules
.idea
.editorconfig

View File

@@ -1,4 +1,5 @@
# 小海博客前端页面
![BuildProject](https://github.com/xiaohai2271/blog-frontEnd/workflows/BuildProject/badge.svg)
基于angular的前端展示页面
@@ -31,12 +32,17 @@
> 普通用户后台
![普通用户后台](./pic/admin-user.png)
![普通用户后台](./pic/user.png)
> 写作页面
![普通用户后台](./pic/write.png)
> 登录页面
![](./pic/login.png)
> 登录页面的背景图采用bing的图片每日一更哦。
@@ -45,7 +51,7 @@
##### 构建
> - index
>
>
> > 1. 进入index目录
> > 2. npm install
@@ -55,15 +61,8 @@
> > - `/src/environments/environment-prod.ts`(线上发布环境)
> > 4. ng build --prod
>
> - admin
>
> > 1. cd admin
> > 2. npm install
> > 3. 修改环境数据中的host
> > - ` /src/environments/environment.ts` (本地开发环境)
> > - `/src/environments/environment-prod.ts`(线上发布环境)
> > 4. ng build --prod
> > 5. 修改index.html将` <base href="/">`改为 ` <base href="/admin/">`
>可使用项目根目录的`build.sh` 脚本进行构建,但是 两个项目中的环境里面的变量仍需自己修改
>
>
@@ -71,27 +70,11 @@
-`index/dist/index`下的全部文件上传到网站根目录
-`admin/dist/admin`文件夹上传到根目录
- 目录结构如下:
![目录结构](./pic/prodfiletree.jpg)
- 修改nginx的location配置
```nginx
location ~ /admin/* {
try_files $uri $uri /admin/index.html;
}
location / {
location / {
try_files $uri $uri/ /index.html;
}
}
```
#### 📝TODO
#### 📌FIXME

View File

@@ -1,27 +0,0 @@
# Admin
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.0.4.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@@ -1,128 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"admin": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/admin/",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*",
"input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
"output": "/assets/"
}
],
"styles": [
"./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "admin:build"
},
"configurations": {
"production": {
"browserTarget": "admin:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "admin:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "admin:serve"
},
"configurations": {
"production": {
"devServerTarget": "admin:serve:production"
}
}
}
}
}
},
"defaultProject": "admin"
}

View File

@@ -1,12 +0,0 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

View File

@@ -1,32 +0,0 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@@ -1,23 +0,0 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to admin!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View File

@@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}

View File

@@ -1,13 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View File

@@ -1,32 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/admin'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

9678
admin/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +0,0 @@
{
"name": "admin",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~8.0.1",
"@angular/common": "~8.0.1",
"@angular/compiler": "~8.0.1",
"@angular/core": "~8.0.1",
"@angular/forms": "~8.0.1",
"@angular/platform-browser": "~8.0.1",
"@angular/platform-browser-dynamic": "~8.0.1",
"@angular/router": "~8.0.1",
"ng-zorro-antd": "^8.1.0",
"rxjs": "~6.4.0",
"tslib": "^1.9.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.800.0",
"@angular/cli": "~8.0.4",
"@angular/compiler-cli": "~8.0.1",
"@angular/language-service": "~8.0.1",
"@types/node": "~8.9.4",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.4.3"
}
}

View File

@@ -1,70 +0,0 @@
import {NgModule} from '@angular/core';
import {NavigationStart, Router, RouterModule, Routes} from '@angular/router';
import {filter} from 'rxjs/operators';
import {UserService} from './services/user/user.service';
import {AIndexComponent} from './pages/a-index/a-index.component';
import {ArticleManagerComponent} from './pages/article-manager/article-manager.component';
import {CommentManagerComponent} from './pages/comment-manager/comment-manager.component';
import {TagManagerComponent} from './pages/tag-manager/tag-manager.component';
import {UserInfoComponent} from './pages/user-info/user-info.component';
import {CategoryManagerComponent} from './pages/category-manager/category-manager.component';
import {LinksManagerComponent} from './pages/links-manager/links.component';
import {VisitorManagerComponent} from './pages/visitor-manager/visitor-manager.component';
import {UpdateManagerComponent} from './pages/update-manager/update.component';
import {UserManagerComponent} from './pages/user-manager/user-manager.component';
const routes: Routes = [
{path: '', component: AIndexComponent},
{path: 'articles', component: ArticleManagerComponent},
{path: 'comments', component: CommentManagerComponent},
{path: 'tags', component: TagManagerComponent},
{path: 'userInfo', component: UserInfoComponent},
{path: 'categories', component: CategoryManagerComponent},
{path: 'links', component: LinksManagerComponent},
{path: 'visitors', component: VisitorManagerComponent},
{path: 'update', component: UpdateManagerComponent},
{path: 'users', component: UserManagerComponent},
{path: '**', component: AIndexComponent}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
// todo 换用路由守卫
constructor(private router: Router, private userService: UserService) {
this.router.events.pipe(filter((event) => event instanceof NavigationStart)
).subscribe((event: NavigationStart) => {
if (!this.userService.userInfo) {
this.userService.getUserInfo().subscribe(data => {
if (data.code === 0) {
this.checkPermission(event);
} else if (data.code === 301) {
window.location.href = '/login';
} else {
window.location.href = '/';
}
});
} else {
this.checkPermission(event);
}
});
}
private checkPermission(event) {
if (this.userService.userInfo.role === 'user') {
switch (event.url) {
case '/':
case '/comments':
case '/userInfo':
break;
default :
this.router.navigateByUrl('/');
}
}
}
}

View File

@@ -1,49 +0,0 @@
.menu {
position: fixed;
max-width: 240px;
top: 50px;
bottom: 0;
left: 0;
overflow-y: auto;
overflow-x: hidden;
}
.myCard {
width: 180px;
margin-left: 10px;
}
.main {
position: absolute;
top: 50px;
right: 0;
left: 240px;
bottom: 0;
}
p {
text-align: center;
}
nz-content {
margin-left: 30px;
margin-right: 30px;
}
.inner-content {
padding: 15px;
background: #fff;
min-height: 560px;
}
nz-footer {
text-align: center;
}
@media only screen and (max-width: 768px) {
nz-content {
margin-left: 10px;
margin-right: 10px;
}
}

View File

@@ -1,96 +0,0 @@
<app-header></app-header>
<div style="height: 60px;"></div>
<nz-layout class="layout">
<nz-sider nzCollapsible
[(nzCollapsed)]="isCollapsed"
[nzBreakpoint]="'lg'"
[nzCollapsedWidth]="0"
[nzZeroTrigger]="zeroTrigger"
nzTheme="light"
*ngIf="userService.userInfo">
<ul nz-menu nzTheme="light" nzMode="inline" [nzInlineCollapsed]="isCollapsed">
<li nz-menu-item routerLink="/">
<i nz-icon nzType="desktop" nzTheme="outline"></i>
<span>后台首页</span>
</li>
<li nz-submenu nzTitle="文章管理" nzIcon="file" *ngIf="userService.userInfo.role=='admin'">
<ul>
<li nz-menu-item>
<a href="../write">
<i nz-icon nzType="form" nzTheme="outline"></i>
<span>新增文章</span>
</a>
</li>
<li nz-menu-item routerLink="/articles">
<i nz-icon nzType="ordered-list" nzTheme="outline"></i>
<span>文章列表</span>
</li>
</ul>
</li>
<li nz-menu-item routerLink="comments">
<i nz-icon nzType="message" nzTheme="outline"></i>
<span>评论管理</span>
</li>
<li nz-menu-item routerLink="tags" *ngIf="userService.userInfo.role=='admin'">
<i nz-icon nzType="tags" nzTheme="outline"></i>
<span>标签管理</span>
</li>
<li nz-menu-item routerLink="categories" *ngIf="userService.userInfo.role=='admin'">
<i nz-icon nzType="appstore" nzTheme="outline"></i>
<span>分类管理</span>
</li>
<li nz-menu-item routerLink="userInfo">
<i nz-icon nzType="idcard" nzTheme="outline"></i>
<span>修改信息</span>
</li>
<li nz-menu-item routerLink="users" *ngIf="userService.userInfo.role=='admin'">
<i nz-icon nzType="user" nzTheme="outline"></i>
<span>用户管理</span>
</li>
<li nz-menu-item routerLink="links" *ngIf="userService.userInfo.role=='admin'">
<i nz-icon nzType="link" nzTheme="outline"></i>
<span>友链管理</span>
</li>
<li nz-menu-item routerLink="visitors" *ngIf="userService.userInfo.role=='admin'">
<i nz-icon nzType="chrome" nzTheme="outline"></i>
<span>访问管理</span>
</li>
<li nz-menu-item routerLink="update" *ngIf="userService.userInfo.role=='admin'">
<i nz-icon nzType="arrow-up" nzTheme="outline"></i>
<span>更新管理</span>
</li>
<!--TODO : do something here ..... -->
<nz-card class="myCard" *ngIf="!isCollapsed&&userService.userInfo.role=='admin'">
<p>别管别人言语</p>
<p>做最好的自己</p>
</nz-card>
<nz-card class="myCard" *ngIf="!isCollapsed&&userService.userInfo.role=='user'">
<p>欢迎来访小海博客</p>
</nz-card>
</ul>
</nz-sider>
<nz-layout>
<nz-content>
<div class="inner-content">
<router-outlet></router-outlet>
</div>
</nz-content>
<nz-footer>© <a href="https://www.celess.cn">小海博客</a> - Design by 小海</nz-footer>
</nz-layout>
</nz-layout>
<ng-template #zeroTrigger>
<i nz-icon nzType="menu-fold" nzTheme=outline></i>
</ng-template>

View File

@@ -1,35 +0,0 @@
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 'admin'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('admin');
});
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 admin!');
});
});

View File

@@ -1,21 +0,0 @@
import {Component, OnInit} 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 implements OnInit {
isCollapsed: boolean = false;
constructor(public userService: UserService) {
}
ngOnInit(): void {
}
}

View File

@@ -1,63 +0,0 @@
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
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 {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
// components
import {HeaderComponent} from './components/header/header.component';
// pages
import {AIndexComponent} from './pages/a-index/a-index.component';
import {ArticleManagerComponent} from './pages/article-manager/article-manager.component';
import {CommentManagerComponent} from './pages/comment-manager/comment-manager.component';
import {TagManagerComponent} from './pages/tag-manager/tag-manager.component';
import {UserInfoComponent} from './pages/user-info/user-info.component';
import {CategoryManagerComponent} from './pages/category-manager/category-manager.component';
import {LinksManagerComponent} from './pages/links-manager/links.component';
import {VisitorManagerComponent} from './pages/visitor-manager/visitor-manager.component';
import {UpdateManagerComponent} from './pages/update-manager/update.component';
import {UserManagerComponent} from './pages/user-manager/user-manager.component';
// services
import {HttpService} from './services/http.service';
registerLocaleData(zh);
@NgModule({
declarations: [
AppComponent,
HeaderComponent,
AIndexComponent,
ArticleManagerComponent,
CommentManagerComponent,
TagManagerComponent,
UserInfoComponent,
CategoryManagerComponent,
LinksManagerComponent,
VisitorManagerComponent,
UpdateManagerComponent,
UserManagerComponent
],
imports: [
BrowserModule,
AppRoutingModule,
NgZorroAntdModule,
FormsModule,
HttpClientModule,
BrowserAnimationsModule
],
providers: [
{provide: NZ_I18N, useValue: zh_CN},
HttpService
],
bootstrap: [AppComponent]
})
export class AppModule {
}

View File

@@ -1,19 +0,0 @@
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;
}

View File

@@ -1,6 +0,0 @@
export class Category {
id: number;
name: string;
articles: string;
size?: number;
}

View File

@@ -1,13 +0,0 @@
export class Comment {
id: number;
type: number;
authorName: string;
authorAvatarImgUrl: string;
content: string;
date: string;
pid: number;
responseId: string;
articleID: number;
articleTitle: string;
child: Comment[];
}

View File

@@ -1,19 +0,0 @@
export class CommentReq {
articleID: number;
comment: boolean;
content: string;
id: number;
pid: number;
responseId: string;
constructor(comment: boolean) {
this.comment = comment;
this.responseId = '';
if (!comment) {
this.articleID = -1;
}
this.pid = -1;
this.id = null;
}
}

View File

@@ -1,6 +0,0 @@
export class Data {
code: number;
msg: string;
result: any;
date: number;
}

View File

@@ -1,6 +0,0 @@
export class Link {
id: number;
name: string;
url: string;
open: boolean;
}

View File

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

View File

@@ -1,6 +0,0 @@
export class Tag {
id?: number;
name: string;
articles?: string;
size: number;
}

View File

@@ -1,11 +0,0 @@
export class User {
id: number;
email: string;
displayName: string;
emailStatus: boolean;
avatarImgUrl: string;
recentlyLandedDate: string;
desc: string;
role: string;
pwd?: string;
}

View File

@@ -1,129 +0,0 @@
header {
height: 50px;
background-color: #ffffff;
opacity: 0.8;
position: fixed;
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;
}
@media only screen and (min-width: 768px) {
/**导航栏*/
.nav-desktop {
position: fixed;
width: auto;
left: 50%;
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: 30px;
}
}

View File

@@ -1,40 +0,0 @@
<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>
<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="../">博客首页</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">
<div class="login">
<a class="headerButton" style="border-radius: 8px;color: white"
id="index-topbar-forLogin" (click)="toPage('login')">登录</a>
</div>
<div class="registration">
<a class="headerButton" style="border-radius: 8px;color: white" (click)="toPage('registration')">注册</a>
</div>
</div>
</header>

View File

@@ -1,30 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {UserService} from '../../services/user/user.service';
import {environment} from '../../../environments/environment';
@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;
ngOnInit() {
}
logout() {
this.userService.logout();
window.location.href = '/';
}
toPage(path: string) {
window.location.href = path;
}
}

View File

@@ -1,50 +0,0 @@
.dashboardUl {
width: 100%;
padding: 1.6em;
}
.dashboardUl i {
font-size: 40px;
}
.dashboardUl li {
float: left;
border-right: 1px solid #ececec;
width: 25%;
text-align: center;
}
.dashboardUl li:last-child {
border-right: none;
}
.circle {
border-radius: 50%;
text-align: center;
}
p {
margin: 15px 0;
}
.log {
margin-top: 20px;
max-height: 500px;
padding: 10px;
background: #F8F8F8;
}
.reloadLog {
position: absolute;
right: 20px;
top: 10px;
z-index: 990;
}
.card {
text-align: center;
}
.card i {
font-size: 40px;
}

View File

@@ -1,71 +0,0 @@
<!-- content start -->
<div class="admin-content" id="index">
<div class="admin-content-body" *ngIf="userService.userInfo&&userService.userInfo.role=='user'">
<div>
<div><strong class="part-title">首页</strong></div>
</div>
<h2 style="text-align: center;">
Welcome {{userService.userInfo.displayName}}!
</h2>
<nz-card class="card">
<ul class="dashboardUl">
<li style="width: 50%">
<div><i nz-icon nzType="login" nzTheme="outline"></i></div>
<p>上次登录</p>
<p>{{userService.userInfo.recentlyLandedDate}}</p>
</li>
<li style="width: 50%">
<div><i nz-icon nzType="environment" nzTheme="outline"></i></div>
<p>您的ip和位置</p>
<p>
ip:{{ip}}
<br>
位置:{{location}}
</p>
</li>
</ul>
</nz-card>
</div>
<!-- administrator page -->
<div *ngIf="userService.userInfo&&userService.userInfo.role=='admin'">
<nz-card>
<ul class="dashboardUl">
<li>
<div><i nz-icon nzType="ie" nzTheme="outline"></i></div>
<p>总访问量</p>
<p>{{visitorService.totalVisitCount}}</p>
</li>
<li>
<div><i nz-icon nzType="chrome" nzTheme="outline"></i></div>
<p>今日访问量</p>
<p>{{visitorService.dayVisit}}</p>
</li>
<li>
<div><i nz-icon nzType="login" nzTheme="outline"></i></div>
<p>上次登录</p>
<p>{{userService.userInfo.recentlyLandedDate}}</p>
</li>
<li>
<div><i nz-icon nzType="arrow-up" nzTheme="outline"></i></div>
<p>上次更新</p>
<p>{{webUpdateService.lastestUpdateTime}}</p>
</li>
</ul>
</nz-card>
<div style="position: relative">
<button nz-button nzType="danger" nzShape="circle" class="reloadLog" (click)="readLog()">
<i nz-icon nzType="reload" nzTheme="outline"></i>
</button>
<nz-spin [nzSpinning]="Loading">
<pre class="log">
{{this.logService.logText}}
</pre>
</nz-spin>
</div>
</div>
</div>
<!-- content end -->

View File

@@ -1,80 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {UserService} from '../../services/user/user.service';
import {VisitorService} from '../../services/visitor/visitor.service';
import {WebUpdateService} from '../../services/update/web-update.service';
import {LogService} from '../../services/log/log.service';
import {Observable} from 'rxjs';
@Component({
selector: 'app-a-index',
templateUrl: './a-index.component.html',
styleUrls: ['./a-index.component.css']
})
export class AIndexComponent implements OnInit {
constructor(public userService: UserService,
public visitorService: VisitorService,
public webUpdateService: WebUpdateService,
public logService: LogService) {
}
Loading: boolean = false;
ip: string;
location: string;
ngOnInit() {
if (this.userService.userInfo) {
if (this.userService.userInfo.role === 'admin') {
this.admin();
} else {
this.user();
}
} else {
this.userService.getUserInfo().subscribe(data => {
if (data.result.role === 'admin') {
this.admin();
} else {
this.user();
}
});
}
}
admin() {
if (!this.visitorService.dayVisit) {
this.visitorService.getDayVisitor();
}
this.visitorService.getTotalVisitorCount();
if (!this.webUpdateService.lastestUpdateTime) {
this.webUpdateService.getLastestUpdateTime();
}
this.readLog();
}
user() {
this.visitorService.getLocalIp().subscribe(data => {
if (data.code === 0) {
this.ip = data.result;
const getip = this.visitorService.getIp(this.ip);
if (getip instanceof Observable) {
getip.subscribe(ipOb => {
this.location = ipOb.result;
});
} else {
this.location = getip;
}
}
});
}
readLog() {
this.Loading = true;
this.logService.getLog().subscribe(date => {
setTimeout(() => {
this.Loading = false;
}, 100);
});
}
}

View File

@@ -1,6 +0,0 @@
.popup-content {
text-align: center;
height: 70px;
}

View File

@@ -1,73 +0,0 @@
<!--文章管理-->
<div class="admin-content" id="article-manager">
<div class="admin-content-body">
<div>
<div><strong class="part-title">文章管理</strong></div>
</div>
<div class="scrollable-horizontal" *ngIf="articleService.currentPage">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>标题</th>
<th>发布日期</th>
<th>更新日期</th>
<th>阅读量</th>
<th title="(可见/不可见)勾选则可见,否则不可见">文章状态</th>
<th>管理</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let article of articleService.currentPage.list">
<td>{{article.id}}</td>
<td><a [routerLink]="[ '/article']" [queryParams]="{'id':article.id}">{{article.title}}</a>
</td>
<td>{{article.publishDateFormat}}</td>
<td>{{article.updateDateFormat}}</td>
<td><span class="badge badge-success">{{article.readingNumber}}</span></td>
<td style="text-align: center">
<input type="checkbox" class="disabled" disabled [checked]="article.open"
[title]="article.open?'可见':'不可见'">
</td>
<td>
<a [href]="'../write?id='+article.id">
<i nz-icon nzType="edit" nzTheme="twotone" nzTwotoneColor="#52c41a"></i> 编辑
</a>
<nz-divider nzType="vertical"></nz-divider>
<a nz-dropdown [nzDropdownMenu]="menu" nzPlacement="bottomLeft" nzTrigger="click">
更多 <i nz-icon nzType="down" nzTheme="outline"></i>
</a>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>
<li nz-menu-item>
<a [href]="'../article/'+article.id">
<i nz-icon nzType="file" nzTheme="twotone" nzTwotoneColor="#3F66FF"></i> 查看
</a>
</li>
<li nz-menu-item>
<a nz-popconfirm [nzTitle]="'是否要删除'+article.title+'?'"
(nzOnConfirm)="doDel(article.id)">
<i nz-icon nzType="delete" nzTheme="twotone" nzTwotoneColor="#eb2f96"></i> 删除
</a>
</li>
</ul>
</nz-dropdown-menu>
</td>
</tr>
</tbody>
</table>
<nz-pagination align="center" [nzHideOnSinglePage]="true" [nzPageIndex]="pageNum" [nzPageSize]="pageSize"
[nzTotal]="articleService.currentPage.total"
(nzPageIndexChange)="toPage($event)">
</nz-pagination>
</div>
</div>
</div>

View File

@@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ArticleManagerComponent } from './article-manager.component';
describe('ArticleManagerComponent', () => {
let component: ArticleManagerComponent;
let fixture: ComponentFixture<ArticleManagerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ArticleManagerComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ArticleManagerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,50 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {NzMessageService} from 'ng-zorro-antd';
import {ArticleService} from '../../services/article/article.service';
@Component({
selector: 'app-article-manager',
templateUrl: './article-manager.component.html',
styleUrls: ['./article-manager.component.css']
})
export class ArticleManagerComponent implements OnInit {
constructor(private message: NzMessageService,
public articleService: ArticleService) {
}
public pageNum = 1;
public pageSize = 10;
showPupup = false;
ngOnInit() {
this.getData();
}
getData() {
this.articleService.getArticle(this.pageNum, this.pageSize);
}
doDel(id) {
this.showPupup = false;
this.articleService.deleteArticle(id).subscribe(data => {
if (data.code === 0) {
this.message.success('删除成功');
this.getData();
} else {
this.message.error('失败,原因:' + data.msg);
}
});
}
toPage(e: number) {
this.pageNum = e;
this.getData();
}
}

View File

@@ -1,4 +0,0 @@
a{
padding: 10px 0;
z-index: 5;
}

View File

@@ -1,66 +0,0 @@
<!--分类-->
<div class="admin-content" id="category-manager">
<div class="admin-content-body">
<div>
<strong class="part-title">分类管理</strong>
<button nz-button nzGhost="true" nzType="primary" style="margin-left: 30px" (click)="addNewOne()">新增分类
</button>
</div>
<div class="scrollable-horizontal" *ngIf="categoryService.categories">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>分类名称</th>
<th>分类文章</th>
<th>文章数目</th>
<th>管理</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let category of categoryService.categories">
<td>{{category.id}}</td>
<td class="text-truncate" style="max-width: 150px">{{category.name}}</td>
<td>{{category.articles}}</td>
<td><span class="badge badge-success">{{category.articles.split(",").length - 1}}</span>
</td>
<td>
<a [href]="'../category?name='+category.name">
<i nz-icon nzType="book" nzTheme="twotone"></i> 查看
</a>
<nz-divider nzType="vertical"></nz-divider>
<a nz-dropdown [nzDropdownMenu]="menu" nzPlacement="bottomLeft" nzTrigger="click">
更多 <i nz-icon nzType="down" nzTheme="outline"></i>
</a>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>
<li nz-menu-item>
<a class="btn btn-secondary " (click)="edit(category.id,category.name)">编辑</a>
</li>
<li nz-menu-item>
<a nz-popconfirm nzTitle="是否要删除这个分类?" (nzOnConfirm)="doDel(category.id)">删除
</a>
</li>
</ul>
</nz-dropdown-menu>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<nz-modal [(nzVisible)]="showPupup" nzTitle="编辑" (nzOnCancel)="cancel()" (nzOnOk)="submit()">
<input nz-input style="width: 80%;margin-left: 10%" [(ngModel)]="updateReqBody.name">
</nz-modal>

View File

@@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CategoryManagerComponent } from './category-manager.component';
describe('CategoryManagerComponent', () => {
let component: CategoryManagerComponent;
let fixture: ComponentFixture<CategoryManagerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CategoryManagerComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CategoryManagerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,95 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {NzMessageService} from 'ng-zorro-antd';
import {CategoryService} from '../../services/category/category.service';
@Component({
selector: 'app-category-manager',
templateUrl: './category-manager.component.html',
styleUrls: ['./category-manager.component.css']
})
export class CategoryManagerComponent implements OnInit {
constructor(public categoryService: CategoryService, private message: NzMessageService) {
}
showPupup = false;
updateReqBody = {
id: null,
name: null
};
isAddNowOne = false; // 是否是新增分类的标识
ngOnInit() {
this.getData();
}
getData() {
if (this.categoryService.categories) {
return;
}
this.categoryService.getAllCategory();
}
edit(id, name) {
this.showPupup = true;
this.updateReqBody.id = id;
this.updateReqBody.name = name;
this.isAddNowOne = false;
}
submit() {
if (this.updateReqBody.name === '') {
alert('不能修改为空值!');
return;
}
this.showPupup = false;
if (!this.isAddNowOne) {
// 更新
this.categoryService.update(this.updateReqBody).subscribe(data => {
if (data.code === 0) {
this.message.success('修改成功');
for (let i = 0; i < this.categoryService.categories.length; i++) {
if ( this.categoryService.categories[i].id === this.updateReqBody.id) {
this.categoryService.categories[i].name = this.updateReqBody.name;
}
}
} else {
this.message.error('修改失败,原因:' + data.msg);
}
});
} else {
// 新建
this.categoryService.create(this.updateReqBody.name).subscribe(data => {
if (data.code === 0) {
this.message.success('新增成功');
} else {
this.message.error('新增失败,原因:' + data.msg);
}
});
}
}
doDel(id) {
this.categoryService.delete(id).subscribe(data => {
if (data.code === 0) {
this.message.success('删除成功');
} else {
this.message.error('删除失败,原因:' + data.msg);
}
});
}
addNewOne() {
this.showPupup = true;
this.updateReqBody.name = name;
this.isAddNowOne = true;
}
cancel() {
this.showPupup = !this.showPupup;
}
}

View File

@@ -1,16 +0,0 @@
.modal-mask {
background: black;
opacity: 0.4;
position: absolute;
overflow: hidden;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.comment {
border: 1px solid #cccccc;
border-radius: 5px;
padding: 5px 10px;
}

View File

@@ -1,174 +0,0 @@
<!--评论管理-->
<div class="admin-content" id="comment-manager">
<div class="admin-content-body">
<div><strong class="part-title">评论管理</strong>
<select class="select" [(ngModel)]="commentType" (change)="doInquire()">
<option class="option" selected value="1">评论</option>
<option class="option" value="0">留言</option>
</select>
</div>
<!-- 评论 -->
<div *ngIf="commentType==1&&commentService.currentComment">
<div class="scrollable-horizontal">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>文章标题</th>
<th>评论内容</th>
<th>评论日期</th>
<th>管理</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let comment of commentService.currentComment.list">
<td>{{comment.id}}</td>
<td class="text-truncate" [title]="comment.articleID+'.'+comment.articleTitle">
<a [routerLink]="[ '/article']"
[queryParams]="{'id':comment.articleID}">{{comment.articleTitle}}</a>
</td>
<td [title]="comment.content">
<span>{{comment.content.length > 40 ? (comment.content.substr(0, 40) + '...') : comment.content}}</span>
<nz-divider nzType="vertical"></nz-divider>
<a style="float: right;clear: both" (click)="prepareComment(comment)">查看</a>
</td>
<td>{{comment.date}}</td>
<td>
<a (click)="edit(0,comment.id,comment.content)">
<i nz-icon nzType="edit" nzTheme="twotone" nzTwotoneColor="#52c41a"></i> 编辑
</a>
<a nz-dropdown [nzDropdownMenu]="menu" nzPlacement="bottomLeft" nzTrigger="click">
更多 <i nz-icon nzType="down" nzTheme="outline"></i>
</a>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>
<!-- <li nz-menu-item>-->
<!-- <a disabled="true">回复</a>-->
<!-- </li>-->
<li nz-menu-item>
<a nz-popconfirm nzTitle="是否要删除这条评论?"
(nzOnConfirm)="doDel(comment.id)">删除
</a>
</li>
</ul>
</nz-dropdown-menu>
</td>
</tr>
</tbody>
</table>
<nz-pagination align="center" [nzHideOnSinglePage]="true" [nzPageIndex]="pageNum"
[nzTotal]="commentService.currentComment.total"
[nzPageSize]="pageSize" (nzPageIndexChange)="toPage($event)">
</nz-pagination>
</div>
</div>
<!-- 留言 -->
<div *ngIf="commentType==0&&commentService.currentLeaveMsg">
<div class=" scrollable-horizontal">
<table class="table ">
<thead>
<tr>
<th>ID</th>
<th>留言者</th>
<th>留言内容</th>
<th>留言日期</th>
<th>管理</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let leaveMsg of commentService.currentLeaveMsg.list">
<td>{{leaveMsg.id}}</td>
<td>{{leaveMsg.authorName}}</td>
<td [title]="leaveMsg.content">
<span>{{leaveMsg.content.length > 40 ? (leaveMsg.content.substr(0, 40) + '...') : leaveMsg.content}}</span>
<a style="float: right;clear: both" (click)="prepareComment(leaveMsg)">查看</a>
</td>
<td>{{leaveMsg.date}}</td>
<td>
<a (click)="edit(1,leaveMsg.id,leaveMsg.content)">
<i nz-icon nzType="edit" nzTheme="twotone" nzTwotoneColor="#52c41a"></i> 编辑
</a>
<nz-divider nzType="vertical"></nz-divider>
<a nz-dropdown [nzDropdownMenu]="menu" nzPlacement="bottomLeft" nzTrigger="click">
更多 <i nz-icon nzType="down" nzTheme="outline"></i>
</a>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>
<!-- <li nz-menu-item>-->
<!-- <a disabled="true">回复</a>-->
<!-- </li>-->
<li nz-menu-item>
<a nz-popconfirm nzTitle="是否要删除这条留言?"
(nzOnConfirm)="doDel(leaveMsg.id)">删除
</a>
</li>
</ul>
</nz-dropdown-menu>
</td>
</tr>
</tbody>
</table>
<nz-pagination align="center" [nzHideOnSinglePage]="true" [nzPageIndex]="pageNum"
[nzTotal]="commentService.currentLeaveMsg.total"
[nzPageSize]="pageSize" (nzPageIndexChange)="toPage($event)">
</nz-pagination>
</div>
</div>
</div>
</div>
<!-- 带输入框的弹出层 -->
<nz-modal [(nzVisible)]="showPupup" nzTitle="编辑" (nzOnCancel)="showPupup=!showPupup" (nzOnOk)="update()">
<textarea nz-input [(ngModel)]="updateReqBody.content"
[nzAutosize]="{ minRows: 2, maxRows: 6 }"></textarea>
</nz-modal>
<nz-modal [(nzVisible)]="showCommentDetail" (nzOnCancel)="showCommentDetail=!showCommentDetail"
(nzOnOk)="showCommentDetail=!showCommentDetail">
<nz-comment [nzAuthor]="commentDetail.authorName"
[nzDatetime]="commentDetail.date">
<nz-avatar nz-comment-avatar nzIcon="user" [nzSrc]="commentDetail.authorAvatarImgUrl"></nz-avatar>
<nz-comment-content class="comment">
<p>{{commentDetail.content}}</p>
</nz-comment-content>
<ng-container *ngIf="commentDetail.child && commentDetail.child.length">
<nz-comment *ngFor="let secLeaveMsg of commentDetail.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>
</ng-container>
<nz-comment>
<nz-comment-content>
<nz-form-item>
<textarea [(ngModel)]="responseComment.content" nz-input
[nzAutosize]="{ minRows: 2, maxRows: 6 }"></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-modal>

View File

@@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CommentManagerComponent } from './comment-manager.component';
describe('CommentManagerComponent', () => {
let component: CommentManagerComponent;
let fixture: ComponentFixture<CommentManagerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CommentManagerComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CommentManagerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,130 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {NzMessageService} from 'ng-zorro-antd';
import {UserService} from '../../services/user/user.service';
import {CommentService} from '../../services/comment/comment.service';
import {Comment} from '../../classes/comment';
import {CommentReq} from '../../classes/commentReq';
@Component({
selector: 'app-comment-manager',
templateUrl: './comment-manager.component.html',
styleUrls: ['./comment-manager.component.css']
})
export class CommentManagerComponent implements OnInit {
constructor(public userService: UserService,
public commentService: CommentService,
private message: NzMessageService) {
}
pageNum: number = 1;
pageSize: number = 10;
commentType: number = 0;
showPupup = false;
updateReqBody = {
id: null,
type: null,
content: null
};
showCommentDetail: boolean = false;
commentDetail: Comment = new Comment();
responseComment: CommentReq = new CommentReq(true);
ngOnInit() {
if (!this.userService.userInfo) {
setTimeout(() => {
this.doInquire();
}, 500);
} else {
this.doInquire();
}
}
doInquire() {
const isAdmin: boolean = this.userService.userInfo.role === 'admin';
if (this.commentType) {
this.commentService.getComments(this.pageNum, this.pageSize, isAdmin);
} else {
this.commentService.getLeaveMsg(this.pageNum, this.pageSize, isAdmin);
}
}
toPage(e: number) {
this.pageNum = e;
this.doInquire();
}
prepareComment(comment: Comment) {
this.showCommentDetail = true;
this.commentDetail = comment;
this.commentService.getByPid(comment.id).subscribe(data => {
if (data.code === 0) {
this.commentDetail.child = data.result.list;
}
});
// tslint:disable-next-line:triple-equals
this.responseComment.comment = this.commentType == 1;
// 若展示的是顶级评论 则pid为顶级评论的id 否则为该二级评论的pid
this.responseComment.pid = this.commentDetail.pid === -1 ? this.commentDetail.id : this.commentDetail.pid;
this.responseComment.articleID = this.commentDetail.articleID;
console.log(`${this.commentType.valueOf() == 1} and type is ${this.commentType}`);
}
edit(type, id, content) {
this.showPupup = true;
this.updateReqBody.id = id;
this.updateReqBody.type = type;
this.updateReqBody.content = content;
}
update() {
if (this.updateReqBody.content === '') {
this.message.warning('不能为空');
return;
}
this.showPupup = false;
this.commentService.update(this.updateReqBody).subscribe(data => {
if (data.code === 0) {
this.message.success('修改成功!');
}
});
}
doDel(id) {
this.commentService.delete(id).subscribe(data => {
if (data.code === 0) {
this.message.success('删除成功');
} else {
this.message.error('删除失败,原因:' + data.msg);
}
});
}
reply() {
if (this.responseComment.content == null || this.responseComment.content === '') {
this.message.info('内容不能为空');
return;
}
this.responseComment.content = `@${this.commentDetail.authorName} ${this.responseComment.content}`
this.commentService.rely(this.responseComment).subscribe(data => {
if (data.code === 0) {
this.commentDetail.child.push(data.result);
this.message.success('回复成功');
this.responseComment = new CommentReq(true);
// tslint:disable-next-line:triple-equals
if (this.commentType == 1) {
this.commentService.currentComment.list.push(data.result);
} else {
this.commentService.currentLeaveMsg.list.push(data.result);
}
}
});
}
}

View File

@@ -1,59 +0,0 @@
<div class="admin-content" id="links-manager">
<div class="admin-content-body">
<div>
<strong class="part-title">友链管理</strong>
<button nz-button nzType="primary" nzGhost="true" style="margin-left: 30px;" (click)="add()">新增</button>
</div>
<div>
<div class=" scrollable-horizontal" *ngIf="linkService.currentPage">
<table class="table ">
<thead>
<tr>
<th>ID</th>
<th>友链名称</th>
<th>友链链接</th>
<th>状态</th>
<th>管理</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let link of linkService.currentPage.list">
<td>{{link.id}}</td>
<td class="text-truncate" style="max-width: 150px" [title]="link.name">{{link.name}}</td>
<td><a [href]="link.url" target="_blank">{{link.url}}</a></td>
<td><input type="checkbox" [checked]="link.open" disabled="disabled"></td>
<td>
<div class="btn-group">
<a (click)="edit(link)">编辑</a>
<nz-divider nzType="vertical"></nz-divider>
<a nz-popconfirm nzTitle="是否要删除这条友链?" (nzOnConfirm)="doDel(link.id)">删除</a>
</div>
</td>
</tr>
</tbody>
<nz-pagination align="center" [nzPageIndex]="pageNum" [nzHideOnSinglePage]="true"
[nzTotal]="linkService.currentPage.total" [nzPageSize]="pageSize"
(nzPageIndexChange)="toPage($event)"></nz-pagination>
</table>
</div>
</div>
</div>
</div>
<!-- 带输入框的弹出层 -->
<nz-modal [(nzVisible)]="showPupup" nzTitle="友链编辑" (nzOnCancel)="showPupup=!showPupup" (nzOnOk)="update()">
<label style="margin-left: 10%;">网站名称:</label>
<input nz-input style="width: 80%;margin-left: 10%" [(ngModel)]="updateReqBody.name">
<label style="margin-left: 10%;">网站链接:</label>
<input nz-input style="width: 80%;margin-left: 10%" [(ngModel)]="updateReqBody.url">
<label style="margin-left: 10%;">是否公开显示:</label>
<select [(ngModel)]="updateReqBody.open">
<option value="">请选择</option>
<option value="true">显示</option>
<option value="false">不显示</option>
</select>
</nz-modal>

View File

@@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LinksManagerComponent } from './links.component';
describe('LinksComponent', () => {
let component: LinksManagerComponent;
let fixture: ComponentFixture<LinksManagerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LinksManagerComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LinksManagerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,104 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {NzMessageService} from 'ng-zorro-antd';
import {LinkService} from '../../services/link/link.service';
import {Link} from '../../classes/link';
@Component({
selector: 'app-links',
templateUrl: './links.component.html',
styleUrls: ['./links.component.css']
})
export class LinksManagerComponent implements OnInit {
constructor(public linkService: LinkService, private message: NzMessageService) {
}
pageNum: number = 1;
pageSize: number = 10;
showPupup = false;
updateReqBody = new Link();
isUpdate;
ngOnInit() {
this.getData();
}
getData() {
this.linkService.getLinks(this.pageNum, this.pageSize);
}
toPage(e: number) {
this.pageNum = e;
this.getData();
}
edit(link) {
this.showPupup = true;
this.updateReqBody = link;
this.isUpdate = true;
}
update() {
if (this.updateReqBody.name === '') {
this.message.warning('友链名称不能为空');
return;
}
if (this.updateReqBody.url === '') {
this.message.warning('友链链接不能为空');
return;
}
// @ts-ignore
// tslint:disable-next-line:triple-equals
if (this.updateReqBody.open == '') {
this.message.warning('友链状态不能为空');
return;
}
this.showPupup = false;
if (this.isUpdate) {
this.linkService.update(this.updateReqBody).subscribe(data => {
if (data.code === 0) {
this.message.success('更新成功');
this.getData();
} else {
this.message.error('更新失败,原因:' + data.msg);
}
this.updateReqBody.name = null;
this.updateReqBody.url = null;
this.updateReqBody.open = null;
});
} else {
this.linkService.create(this.updateReqBody).subscribe(data => {
if (data.code === 0) {
this.message.success('新增成功');
this.getData();
} else {
this.message.error('新增失败,原因:' + data.msg);
}
this.updateReqBody.name = null;
this.updateReqBody.url = null;
this.updateReqBody.open = null;
});
}
}
add() {
this.showPupup = true;
this.isUpdate = false;
}
doDel(id) {
this.linkService.delete(id).subscribe(data => {
if (data.code === 0) {
this.message.success('删除成功!');
this.getData();
} else {
this.message.error('删除失败,原因:' + data.msg);
}
});
}
}

View File

@@ -1,11 +0,0 @@
#publishArticle{
width: 100%;
height: 32px;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
border: none;
background: turquoise;
color: white
}

View File

@@ -1,68 +0,0 @@
<!--标签-->
<div class="admin-content" id="tag-manager">
<div class="admin-content-body">
<div>
<div><strong class="part-title">标签管理</strong></div>
</div>
<div class="g">
<div class="scrollable-horizontal" *ngIf="tagService.currentTagPage">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>标签名称</th>
<th>标签文章</th>
<th>文章数目</th>
<th>管理</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let tag of tagService.currentTagPage.list">
<td>{{tag.id}}</td>
<td class="text-truncate" style="max-width: 150px" [title]="tag.name">{{tag.name}}</td>
<td>{{tag.articles}}</td>
<td><span class="badge badge-success">{{tag.articles.split(',').length - 1}}</span></td>
<td>
<a [href]="'../tag?name='+tag.name">
<i nz-icon nzType="tag" nzTheme="twotone"></i> 查看
</a>
<nz-divider nzType="vertical"></nz-divider>
<a nz-dropdown [nzDropdownMenu]="menu" nzPlacement="bottomLeft" nzTrigger="click">
更多 <i nz-icon nzType="down" nzTheme="outline"></i>
</a>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>
<li nz-menu-item>
<a (click)="edit(tag.id,tag.name)">编辑</a>
</li>
<li nz-menu-item>
<a nz-popconfirm nzTitle="是否要删除这条标签?" (nzOnConfirm)="doDel(tag.id)">删除</a>
</li>
</ul>
</nz-dropdown-menu>
</td>
</tr>
</tbody>
</table>
<nz-pagination align="center" [nzPageIndex]="pageNum" [nzHideOnSinglePage]="true"
[nzTotal]="tagService.currentTagPage.total" [nzPageSize]="pageSize"
(nzPageIndexChange)="toPage($event)"></nz-pagination>
</div>
</div>
</div>
</div>
<!-- 带输入框的弹出层 -->
<nz-modal [(nzVisible)]="showPupup" nzTitle="编辑" (nzOnCancel)="showPupup=!showPupup" (nzOnOk)="update()">
<input class="select" style="width: 80%;margin-left: 10%" [(ngModel)]="updateReqBody.name">
</nz-modal>

View File

@@ -1,72 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {NzMessageService} from 'ng-zorro-antd';
import {TagService} from '../../services/tag/tag.service';
@Component({
selector: 'app-tag-manager',
templateUrl: './tag-manager.component.html',
styleUrls: ['./tag-manager.component.css']
})
export class TagManagerComponent implements OnInit {
constructor(public tagService: TagService, private message: NzMessageService) {
}
pageNum: number = 1;
pageSize: number = 10;
showPupup: boolean = false;
updateReqBody = {
id: null,
name: null
};
ngOnInit() {
this.getData();
}
getData() {
this.tagService.getTags(this.pageNum, this.pageSize);
}
toPage(e: number) {
this.pageNum = e;
this.getData();
}
edit(id, name) {
this.showPupup = true;
this.updateReqBody.id = id;
this.updateReqBody.name = name;
}
update() {
if (this.updateReqBody.name === '') {
this.message.warning('不能为空');
return;
}
this.showPupup = false;
this.tagService.update(this.updateReqBody.id, this.updateReqBody.name).subscribe(data => {
if (data.code === 0) {
this.message.success('修改成功!');
this.getData();
}
});
}
doDel(id) {
this.tagService.delete(id).subscribe(data => {
if (data.code === 0) {
this.message.success('删除成功');
this.getData();
} else {
this.message.error(`删除失败,原因:${data.msg}`);
}
});
}
}

View File

@@ -1,54 +0,0 @@
<div class="admin-content" id="tag-manager">
<div class="admin-content-body">
<div>
<strong class="part-title">更新管理</strong>
<button nz-button nzType="primary" nzGhost="true" style="margin-left: 30px;" (click)="add()">新增</button>
</div>
<div>
<div class=" scrollable-horizontal" *ngIf="updateService.updateInfoList">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>更新内容</th>
<th>更新日期</th>
<th>管理</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let update of updateService.updateInfoList.list">
<td>{{update.id}}</td>
<td style="max-width: 150px" nz-tooltip [nzTitle]="update.info">
{{update.info.length > 20 ? update.info.substr(0, 20) + '...' : update.info}}
</td>
<td>{{update.time}}</td>
<td>
<div class="btn-group">
<a (click)="edit(update)">编辑</a>
<nz-divider nzType="vertical"></nz-divider>
<a nz-popconfirm nzTitle="是否要删除这条更新?" (nzOnConfirm)="doDel(update.id)">删除</a>
</div>
</td>
</tr>
</tbody>
</table>
<nz-pagination align="center" [nzPageIndex]="pageNum" [nzHideOnSinglePage]="true"
[nzTotal]="updateService.updateInfoList.total"
[nzPageSize]="pageSize" (nzPageIndexChange)="toPage($event)"></nz-pagination>
</div>
</div>
</div>
</div>
<!-- 带输入框的弹出层 -->
<nz-modal [(nzVisible)]="showPupup" nzTitle="编辑" (nzOnCancel)="showPupup=!showPupup" (nzOnOk)="submit()">
<label style="margin-left: 10%;margin-bottom: 10px">更新内容:</label>
<textarea nz-input style="width: 80%;margin-left: 10%; height: 100px;"
[(ngModel)]="updateReqBody.info" placeholder="更新内容"
[nzAutosize]="{ minRows: 2, maxRows: 6 }"></textarea>
</nz-modal>

View File

@@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UpdateManagerComponent } from './update.component';
describe('UpdateComponent', () => {
let component: UpdateManagerComponent;
let fixture: ComponentFixture<UpdateManagerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [UpdateManagerComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(UpdateManagerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,95 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {NzMessageService} from 'ng-zorro-antd';
import {WebUpdateService} from '../../services/update/web-update.service';
import {UpdateInfo} from "../../classes/updateInfo";
@Component({
selector: 'app-update',
templateUrl: './update.component.html',
styleUrls: ['./update.component.css']
})
export class UpdateManagerComponent implements OnInit {
constructor(public updateService: WebUpdateService, private message: NzMessageService) {
}
isUpdate = false;
pageNum: number = 1;
pageSize: number = 10;
showPupup = false;
updateReqBody = new UpdateInfo();
ngOnInit() {
this.getData();
}
getData() {
this.updateService.getUpdateInfo(this.pageNum, this.pageSize);
}
toPage(e: number) {
this.pageNum = e;
this.getData();
}
edit(link) {
this.showPupup = true;
this.updateReqBody = link;
this.isUpdate = true;
}
submit() {
if (this.updateReqBody.info === '') {
this.message.warning('更新内容不能为空');
return;
}
this.showPupup = false;
if (this.isUpdate) {
this.updateService.update(this.updateReqBody).subscribe(data => {
if (data.code === 0) {
this.message.success('更新成功');
this.getData();
} else {
this.message.error('更新失败,原因:' + data.msg);
}
});
} else {
this.updateReqBody.id = null;
this.updateService.create(this.updateReqBody.info).subscribe(data => {
if (data.code === 0) {
this.message.success('新增成功');
this.getData();
} else {
this.message.error('新增失败,原因:' + data.msg);
}
});
}
this.updateReqBody.id = null;
this.updateReqBody.info = '';
}
add() {
this.showPupup = true;
this.isUpdate = false;
this.updateReqBody.id = null;
this.updateReqBody.info = '';
}
doDel(id: number) {
this.updateService.delete(id).subscribe(data => {
if (data.code === 0) {
this.message.success('删除成功!');
this.getData();
} else {
this.message.error('删除失败,原因:' + data.msg);
}
});
}
}

View File

@@ -1,78 +0,0 @@
.label {
margin-bottom: 20px;
font-weight: bold;
font-size: 1.4em;
text-align: center;
}
table {
width: auto;
}
td {
border: none;
}
tr {
background: none;
}
input {
width: 200px;
border: none;
border-radius: 5px;
background: #eeeeee;
height: 30px;
padding-left: 5px;
}
#user-gender input {
width: auto;
height: auto;
margin-left: 10px;
}
.submit {
width: 80%;
margin-left: 10%;
}
#avatar {
position: absolute;
right: 20px;
top: 30%;
border: 1px solid #999999;
border-radius: 1px;
padding: 10px;
}
#avatarimg {
width: 100px;
height: 100px;
margin: 10px;
}
#info {
width: 350px;
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, 50%);
}
#info i {
margin-right: 10px;
}
#info .sendEmail {
position: absolute;
margin-top: 3px;
top: 0;
right: 15px;
}
#info input {
width: 90%;
margin-bottom: 10px;
height: 40px;
}

View File

@@ -1,46 +0,0 @@
<!--修改信息-->
<div class="admin-content" id="user-info">
<div class="admin-content-body">
<div>
<div><strong class="part-title">个人资料</strong> /
<small>Personal information</small>
</div>
</div>
<hr/>
<div id="info">
<i nz-icon nzType="mail" nzTheme="twotone"></i>
<input nz-input [(ngModel)]="user.email" style="display: inline" [disabled]="true"/>
<button *ngIf="!user.emailStatus" nz-button nzType="primary" class="sendEmail" (click)="sendEmail()"
[title]="'发生邮件到'+user.email+''" [disabled]="!clickable">
发送激活邮件
</button>
<br>
<i nz-icon nzType="info-circle" nzTheme="twotone"></i>
<input nz-input type="text" placeholder="用一句话来描述自己吧" name="desc" id="desc" [(ngModel)]="user.desc">
<br>
<i nz-icon nzType="crown" nzTheme="twotone"></i>
<input nz-input type="text" placeholder="昵称" name="displayName" id="displayName"
[(ngModel)]="user.displayName">
<br>
<button nz-button nzType="primary" nzGhost="true" [nzBlock]="true" (click)="userInfoSubmit()">提交</button>
</div>
<div id="avatar">
<h3><strong>头像</strong></h3>
<div>
<img id="avatarimg" [src]="user.avatarImgUrl" alt="avatar">
</div>
<div>
<nz-upload [nzAction]="host+'/user/imgUpload'" nzWithCredentials="true" nzLimit="1" nzSize="2048"
(nzChange)="avatarUpload($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>选择图片上传</span></button>
</nz-upload>
</div>
</div>
</div>
</div>

View File

@@ -1,74 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {NzMessageService} from 'ng-zorro-antd';
import {UserService} from '../../services/user/user.service';
import {User} from '../../classes/user';
import {environment} from '../../../environments/environment';
@Component({
selector: 'app-user-info',
templateUrl: './user-info.component.html',
styleUrls: ['./user-info.component.css']
})
export class UserInfoComponent implements OnInit {
constructor(public userService: UserService,
private message: NzMessageService) {
}
host: string;
user: User = new User();
clickable: boolean = true;
ngOnInit() {
if (!this.userService.userInfo) {
setTimeout(() => {
this.user = this.userService.userInfo;
}, 500);
} else {
this.user = this.userService.userInfo;
}
this.host = environment.host;
}
userInfoSubmit() {
const info = {
desc: this.user.desc,
displayName: this.user.displayName
};
this.userService.updateInfo(info).subscribe(data => {
if (data.code === 0) {
this.message.success('修改成功');
} else if (data.code === 301) {
window.location.href = '/login';
} else {
this.message.error('修改信息失败,原因:' + data.msg);
}
});
}
sendEmail() {
this.clickable = false;
if (!this.userService.userInfo) {
window.location.href = '/login';
}
this.userService.sendEmail().subscribe(data => {
if (data.code === 0) {
this.message.success('发送成功!请前往邮箱进行激活');
} else {
this.message.error(data.msg);
}
});
}
avatarUpload(info: any) {
if (info.type === 'success' && info.file.response.code === 0) {
const time = new Date().valueOf();
this.userService.userInfo.avatarImgUrl = this.userService.avatarHost + '/' + info.file.response.result;
// 加上时间戳 让图片自动预览,避免读取本地缓存而不显示新头像
this.user.avatarImgUrl = this.userService.avatarHost + '/' + info.file.response.result + '?time=' + time;
}
}
}

View File

@@ -1,7 +0,0 @@
.error {
padding-left: 20px;
color: red;
border: 1px solid #eb2f96;
border-radius: 2px;
display: block;
}

View File

@@ -1,77 +0,0 @@
<!--标签-->
<div class="admin-content" id="tag-manager">
<div class="admin-content-body">
<div>
<div><strong class="part-title">用户管理</strong></div>
</div>
<div class="g">
<div class="scrollable-horizontal" *ngIf="userService.currentUserPage">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>用户邮箱</th>
<th>用户昵称</th>
<th>用户角色</th>
<th>管理</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of userService.currentUserPage.list">
<td align="center">{{user.id}}</td>
<td align="center" class="text-truncate">{{user.email}}</td>
<td align="center">{{user.displayName}}</td>
<td align="center"><span class="badge badge-success">{{user.role}}</span></td>
<td>
<a (click)="edit(user)"><i nz-icon nzType="tag" nzTheme="twotone"></i> 编辑</a>
<nz-divider nzType="vertical"></nz-divider>
<a nz-popconfirm nzTitle="是否要删除这名用户?" (nzOnConfirm)="delete(user.id)">
<i nz-icon nzType="delete" nzTheme="twotone" nzTwotoneColor="#eb2f96"></i> 删除</a>
</td>
</tr>
</tbody>
</table>
<nz-pagination align="center" [nzPageIndex]="pageNum" [nzHideOnSinglePage]="true"
[nzTotal]="userService.currentUserPage.total" [nzPageSize]="pageSize"
(nzPageIndexChange)="toPage($event)"></nz-pagination>
</div>
</div>
</div>
</div>
<!--用户编辑-->
<nz-modal [(nzVisible)]="showPupup" nzTitle="编辑" (nzOnCancel)="showPupup=!showPupup" (nzOnOk)="update()">
<form action="#">
<label for="email">邮箱:</label>
<input nz-input type="email" id="email" name="email" [(ngModel)]="editUser.email" (blur)="getEmailStatus()">
<ng-template [ngIf]="showError">
<span class="error">
<i nz-icon nzType="exclamation-circle" nzTheme="twotone" nzTwotoneColor="#eb2f96"></i>
邮箱已被占用!
</span>
</ng-template>
<label for="display-name">昵称:</label>
<input nz-input type="text" id="display-name" name="display-name" [(ngModel)]="editUser.displayName">
<label for="pwd">密码:<span style="font-weight: lighter;font-size: smaller">留空则不重设密码,反之则设置为输入的密码</span></label>
<input nz-input type="password" id="pwd" name="pwd" [(ngModel)]="editUser.pwd" placeholder="******">
<label for="user-role">角色:</label>
<nz-select name="user-role" style="width: 100%" id="user-role"
[nzDisabled]="editUser.id==userService.userInfo.id" [(ngModel)]="editUser.role">
<nz-option nzValue="user" nzLabel="user"></nz-option>
<nz-option nzValue="admin" nzLabel="admin"></nz-option>
</nz-select>
<label for="email-status">邮箱验证状态:</label>
<br>
<nz-radio-group name="email-status" id="email-status" [(ngModel)]="editUser.emailStatus">
<label nz-radio [nzValue]="true">已验证</label>
<label nz-radio [nzValue]="false">未验证</label>
</nz-radio-group>
<br>
<label for="desc">描述:</label>
<textarea rows="4" id="desc" name="desc" nz-input [(ngModel)]="editUser.desc"></textarea>
</form>
</nz-modal>

View File

@@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UserManagerComponent } from './user-manager.component';
describe('UserManagerComponent', () => {
let component: UserManagerComponent;
let fixture: ComponentFixture<UserManagerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ UserManagerComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(UserManagerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,91 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {UserService} from '../../services/user/user.service';
import {User} from '../../classes/user';
import {NzMessageService} from 'ng-zorro-antd';
@Component({
selector: 'app-user-manager',
templateUrl: './user-manager.component.html',
styleUrls: ['./user-manager.component.css']
})
export class UserManagerComponent implements OnInit {
constructor(public userService: UserService,
private messageService: NzMessageService) {
}
pageNum: number = 1;
pageSize: number = 10;
showPupup: boolean = false;
editUser: User = new User();
showError: boolean = false;
formerEmail: string;
ngOnInit() {
this.userService.getPageUser(this.pageNum, this.pageSize);
}
toPage(e: number) {
this.pageNum = e;
this.userService.getPageUser(this.pageNum, this.pageSize);
}
edit(user: User) {
this.showPupup = true;
this.showError = false;
// 避免引用赋值采用json进行浅拷贝
this.editUser = JSON.parse(JSON.stringify(user));
this.formerEmail = this.editUser.email;
}
delete(id: number) {
this.userService.delete(id).subscribe(data => {
if (data.code === 0) {
if (data.result[0].status) {
this.messageService.success('删除成功!');
this.userService.getPageUser(this.pageNum, this.pageSize);
} else {
this.messageService.error(`删除失败,原因:${data.result[0].msg}`);
}
} else {
this.messageService.error(`请求失败,原因:${data.msg}`);
}
});
}
update() {
this.editUser.recentlyLandedDate = null;
this.editUser.avatarImgUrl = null;
if (this.showError) {
this.showPupup = false;
this.messageService.warning('邮箱不可用,请修改!');
return;
}
this.userService.update(this.editUser).subscribe(data => {
if (data.code === 0) {
this.messageService.success('修改成功!');
if (this.userService.userInfo.id === this.editUser.id) {
this.userService.userInfo = this.editUser;
}
this.userService.getPageUser(this.pageNum, this.pageSize, true);
} else {
this.messageService.error('修改失败!');
}
});
this.showPupup = false;
}
getEmailStatus() {
if (this.editUser.email === this.formerEmail) {
return;
}
this.userService.getExistOfEmail(this.editUser.email).subscribe(data => {
if (data.code === 0) {
this.showError = data.result;
} else {
this.messageService.error(data.msg);
}
});
}
}

View File

@@ -1,49 +0,0 @@
<!--访问量-->
<div class="admin-content" id="visitor-manager">
<div class="admin-content-body">
<div>
<div><strong class="part-title">访问管理</strong>
<span>---当日访问量{{visitorService.dayVisit}}</span>
</div>
</div>
<div class="g">
<div class="scrollable-horizontal" *ngIf="visitorService.currentPage">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>ip地址</th>
<th>访问日期</th>
<th>浏览器类型</th>
<th>系统</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let visitor of visitorService.currentPage.list">
<td>{{visitor.id}}</td>
<td>
<span (mouseenter)="getIp(visitor.ip)" [title]="location">{{visitor.ip}}</span>
<i nz-icon nzType="smile" nzTheme="twotone" nzTwotoneColor="#00DC25"
*ngIf="visitor.ip==localIp" style="float: right;clear: both" title="您的访问记录"></i>
</td>
<td>{{visitor.date}}</td>
<td style="text-align: center" [title]="visitor.browserVersion">
<span *ngIf="visitor.browserName!='Unknown'"> {{visitor.browserName}}</span>
</td>
<td>
<span *ngIf="visitor.osname!='Unknown' "> {{visitor.osname}}</span>
</td>
</tr>
</tbody>
</table>
<nz-pagination align="center " [nzPageIndex]="pageNum " [nzHideOnSinglePage]="true "
[nzTotal]="visitorService.currentPage.total " [nzPageSize]="pageSize "
(nzPageIndexChange)="toPage($event) "></nz-pagination>
</div>
</div>
</div>
</div>

View File

@@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { VisitorManagerComponent } from './visitor-manager.component';
describe('VisitorManagerComponent', () => {
let component: VisitorManagerComponent;
let fixture: ComponentFixture<VisitorManagerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ VisitorManagerComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(VisitorManagerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,58 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {VisitorService} from '../../services/visitor/visitor.service';
@Component({
selector: 'app-visitor-manager',
templateUrl: './visitor-manager.component.html',
styleUrls: ['./visitor-manager.component.css']
})
export class VisitorManagerComponent implements OnInit {
constructor(public visitorService: VisitorService) {
}
pageNum: number = 1;
pageSize: number = 10;
localIp = '';
location: string;
ngOnInit() {
this.getPageData();
this.visitorService.getDayVisitor();
this.visitorService.getLocalIp().subscribe(data => {
this.localIp = data.result;
});
}
getIp(ip) {
const result = this.visitorService.getIp(ip);
if (typeof result === 'string') {
this.location = result;
return;
}
result.subscribe(data => {
if (data.code === 0) {
this.location = data.result;
}
});
}
getPageData() {
this.visitorService.getVisitor(this.pageNum, this.pageSize);
}
toPage(e) {
this.pageNum = e;
this.getPageData();
}
}

View File

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

View File

@@ -1,49 +0,0 @@
import {Injectable} from '@angular/core';
import {HttpService} from '../http.service';
import {Page} from '../../classes/page';
import {Article} from '../../classes/article';
import {Observable, of} from 'rxjs';
import {exist} from '../../utils/dataUtil';
@Injectable({
providedIn: 'root'
})
export class ArticleService {
constructor(public http: HttpService) {
}
// 存储所有已经请求过的数据<首页数据>
pageList: Page<Article>[] = [];
currentPage: Page<Article>;
/**
* 获取文章
* @param pageNum 页码数
* @param pageSize 单页数据量
*/
getArticle(pageNum: number, pageSize: number): object {
const articlePage = exist<Article>(pageNum, pageSize, this.pageList);
if (articlePage) {
articlePage.subscribe(data => {
this.currentPage = data;
});
return articlePage;
}
const observable = this.http.get('/admin/articles?page=' + pageNum + '&count=' + pageSize);
observable.subscribe(data => {
if (data.code === 0) {
this.currentPage = data.result;
this.pageList.push(data.result);
}
});
return observable;
}
deleteArticle(id) {
return this.http.delete('/admin/article/del?articleID=' + id);
}
}

View File

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

View File

@@ -1,56 +0,0 @@
import {Injectable} from '@angular/core';
import {HttpService} from '../http.service';
import {Category} from '../../classes/category';
@Injectable({
providedIn: 'root'
})
export class CategoryService {
// FIXME : !!!!!!!!!!!!!!!!!!数据处理全部放到一个模块中!!!!!!!!!!!!!!!!!!
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;
}
update(submitBody: { id: number, name: string }) {
return this.http.put('/admin/category/update', submitBody, false);
}
create(nameStr: string) {
const observable = this.http.post('/admin/category/create', {name: nameStr}, false);
observable.subscribe(data => {
if (data.code === 0) {
this.categories.push(data.result);
}
});
return observable;
}
delete(id: number) {
const observable = this.http.delete(`/admin/category/del?id=${id}`);
observable.subscribe(data => {
if (data.code === 0) {
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < this.categories.length; i++) {
if (this.categories[i].id === id) {
this.categories.splice(i, 1);
}
}
}
});
return observable;
}
}

View File

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

View File

@@ -1,98 +0,0 @@
import {Injectable} from '@angular/core';
import {HttpService} from '../http.service';
import {Page} from '../../classes/page';
import {Comment} from '../../classes/comment';
import {exist} from '../../utils/dataUtil';
import {CommentReq} from "../../classes/commentReq";
@Injectable({
providedIn: 'root'
})
export class CommentService {
constructor(public http: HttpService) {
}
// 存放
leaveMsgPage: Page<Comment>[] = [];
commentPage: Page<Comment>[] = [];
currentComment: Page<Comment>;
currentLeaveMsg: Page<Comment>;
/**
* 获取 评论
* @param pageNum 页码
* @param pageSize 单页数据数量
* @param isAdmin 是否是管理员
*/
getComments(pageNum: number, pageSize: number, isAdmin: boolean) {
const exist1 = exist<Comment>(pageNum, pageSize, this.commentPage);
if (exist1) {
exist1.subscribe(data => {
this.currentComment = data;
});
return exist1;
}
const observable = this.http.get(`/${isAdmin ? 'admin' : 'user'}/comment/type/1?count=${pageSize}&page=${pageNum}`);
observable.subscribe(data => {
if (data.code === 0) {
this.commentPage.unshift(data.result);
this.currentComment = data.result;
}
});
return observable;
}
/**
* 获取 留言
* @param pageNum 页码
* @param pageSize 单页数据数量
* @param isAdmin 是否是管理员
*/
getLeaveMsg(pageNum: number, pageSize: number, isAdmin: boolean) {
const exist1 = exist<Comment>(pageNum, pageSize, this.leaveMsgPage);
if (exist1) {
exist1.subscribe(data => {
this.currentLeaveMsg = data;
});
return exist1;
}
const observable = this.http.get(`/${isAdmin ? 'admin' : 'user'}/comment/type/0?count=${pageSize}&page=${pageNum}`);
observable.subscribe(data => {
if (data.code === 0) {
this.leaveMsgPage.unshift(data.result);
this.currentLeaveMsg = data.result;
}
});
return observable;
}
/**
* 回复评论/留言
* @param responseComment 请求体
*/
rely(responseComment: CommentReq) {
return this.http.post('/user/comment/create', responseComment, true);
}
/**
* 通过父评论 获取回复
* @param pid 父评论id
*/
getByPid(pid: number) {
return this.http.get('/comment/pid/' + pid + '?count=5&page=1');
}
update(subComment) {
return this.http.put('/user/comment/update', subComment);
}
delete(id: number) {
return this.http.delete('/user/comment/del?id=' + id);
}
}

View File

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

View File

@@ -1,103 +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 '../classes/data';
import {reqBody2Str} from '../utils/dataUtil';
@Injectable({
providedIn: 'root'
})
export class HttpService {
constructor(public httpClient: 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.httpClient.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,
'Content-Type': isJson ? 'application/json' : 'application/x-www-form-urlencoded'
}),
withCredentials: true
};
return this.httpClient.post<Data>(this.getPath(path), isJson ? reqBody : reqBody2Str(reqBody), Options);
}
/**
* put 请求
* @param path 请求路径
* @param reqBody 请求体
* @param isJson 是否发生json格式数据到服务器
*/
put(path: string, reqBody: object, isJson: boolean = false): Observable<Data> {
const Options = {
headers: new HttpHeaders({
Accept: '*/*',
Authorization: this.token,
'Content-Type': isJson ? 'application/json' : 'application/x-www-form-urlencoded'
}),
withCredentials: true
};
return this.httpClient.put<Data>(this.getPath(path), isJson ? reqBody : reqBody2Str(reqBody), Options);
}
/**
* delete 请求
* @param path 请求路径
*/
delete(path: string): Observable<Data> {
return this.httpClient.delete<Data>(this.getPath(path), this.httpOptions);
}
/**
* 检查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;
}
removeToken() {
localStorage.removeItem('token');
this.token = null;
}
}

View File

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

View File

@@ -1,39 +0,0 @@
import {Injectable} from '@angular/core';
import {HttpService} from '../http.service';
import {Link} from '../../classes/link';
import {Page} from '../../classes/page';
@Injectable({
providedIn: 'root'
})
export class LinkService {
constructor(public http: HttpService) {
}
// 不采取存储page[] :: 数据量较少
public currentPage: Page<Link>;
getLinks(pageNum: number, pageSize: number) {
const observable = this.http.get(`/admin/links?page=${pageNum}&count=${pageSize}`);
observable.subscribe(data => {
if (data.code === 0) {
this.currentPage = data.result;
}
});
return observable;
}
update(submitBody: Link) {
return this.http.put('/admin/links/update', submitBody, true);
}
create(submitBody: Link) {
submitBody.id = null;
return this.http.post('/admin/links/create', submitBody, true);
}
delete(id) {
return this.http.delete(`/admin/links/del/${id}`);
}
}

View File

@@ -1,12 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { LogService } from './log.service';
describe('LogService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: LogService = TestBed.get(LogService);
expect(service).toBeTruthy();
});
});

View File

@@ -1,22 +0,0 @@
import {Injectable} from '@angular/core';
import {HttpService} from '../http.service';
@Injectable({
providedIn: 'root'
})
export class LogService {
constructor(private http: HttpService) {
}
logText: string;
getLog() {
// @ts-ignore
const observable = this.http.httpClient.get<string>('https://api.celess.cn/blog.log', {responseType: 'text'});
observable.subscribe(data => {
this.logText = data;
});
return observable;
}
}

View File

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

View File

@@ -1,43 +0,0 @@
import {Injectable} from '@angular/core';
import {HttpService} from '../http.service';
import {Tag} from '../../classes/tag';
import {Page} from '../../classes/page';
import {exist} from '../../utils/dataUtil';
@Injectable({
providedIn: 'root'
})
export class TagService {
constructor(public http: HttpService) {
}
tagPages: Page<Tag>[] = [];
currentTagPage: Page<Tag>;
getTags(pageNum: number, pageSize: number) {
const exist1 = exist<Tag>(pageNum, pageSize, this.tagPages);
if (exist1) {
exist1.subscribe(data => {
this.currentTagPage = data;
});
return exist1;
}
const observable = this.http.get(`/tags?count=${pageSize}&page=${pageNum}`);
observable.subscribe(data => {
if (data.code === 0) {
this.currentTagPage = data.result;
this.tagPages.unshift(data.result);
}
});
}
update(id: number, name: string) {
return this.http.put(`/admin/tag/update?id=${id}&name=${name}`, null);
}
delete(id: number) {
return this.http.delete(`/admin/tag/del?id=${id}`);
}
}

View File

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

View File

@@ -1,48 +0,0 @@
import {Injectable} from '@angular/core';
import {HttpService} from '../http.service';
import {UpdateInfo} from '../../classes/updateInfo';
import {Page} from '../../classes/page';
@Injectable({
providedIn: 'root'
})
export class WebUpdateService {
constructor(public http: HttpService) {
}
public updateInfoList: Page<UpdateInfo>;
public lastestUpdateTime: string;
getUpdateInfo(pageNum: number, pageSize: number) {
const observable = this.http.get(`/webUpdate/pages?page=${pageNum}&count=${pageSize}`);
observable.subscribe((data: any) => {
if (data.code === 0) {
this.updateInfoList = data.result;
}
});
return observable;
}
getLastestUpdateTime() {
this.http.get('/lastestUpdateTime').subscribe(data => {
if (data.code === 0) {
this.lastestUpdateTime = data.result;
}
})
}
update(submitBody: { id: number, info: string }) {
return this.http.put('/admin/webUpdate/update', submitBody, false);
}
create(infoStr: string) {
return this.http.post('/admin/webUpdate/create', {info: infoStr}, false);
}
delete(id: number) {
return this.http.delete(`/admin/webUpdate/del/${id}`);
}
}

View File

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

View File

@@ -1,97 +0,0 @@
import {Injectable} from '@angular/core';
import {HttpService} from '../http.service';
import {User} from '../../classes/user';
import {Page} from '../../classes/page';
import {exist} from '../../utils/dataUtil';
@Injectable({
providedIn: 'root'
})
export class UserService {
userInfo: User;
avatarHost: string = 'http://cdn.celess.cn';
constructor(public http: HttpService) {
this.getUserInfo();
}
userPage: Page<User>[] = [];
currentUserPage: Page<User>;
/**
* 获取用户信息
*/
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();
}
});
}
updateInfo(submitBody: { desc: string, displayName: string }) {
const observable = this.http.put('/user/userInfo/update', submitBody, false);
observable.subscribe(data => {
if (data.code === 0) {
this.userInfo.desc = submitBody.desc;
this.userInfo.displayName = submitBody.displayName;
}
});
return observable;
}
sendEmail() {
return this.http.post('/sendVerifyEmail', {email: this.userInfo.email}, false);
}
/**
* 获取分页数据
* @param pageNum 页码
* @param pageSize 单页数据量
* @param refresh 是否强制刷新
*/
getPageUser(pageNum: number, pageSize: number, refresh: boolean = false) {
const existData = exist<User>(pageNum, pageSize, this.userPage);
if (existData && !refresh) {
existData.subscribe(data => {
this.currentUserPage = data;
});
}
this.http.get(`/admin/users?page=${pageNum}&count=${pageSize}`).subscribe(data => {
if (data.code === 0) {
this.currentUserPage = data.result;
this.userPage.unshift(data.result);
}
});
}
delete(id: number) {
return this.http.delete(`/admin/user/delete/${id}`);
}
update(user: User) {
return this.http.put('/admin/user', user, true);
}
getExistOfEmail(email: string) {
return this.http.get(`/emailStatus/${email}`);
}
}

View File

@@ -1,12 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { VisitorService } from './visitor.service';
describe('VisitorService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: VisitorService = TestBed.get(VisitorService);
expect(service).toBeTruthy();
});
});

View File

@@ -1,76 +0,0 @@
import {Injectable} from '@angular/core';
import {HttpService} from '../http.service';
import {Page} from '../../classes/page';
import {Visitor} from '../../classes/visitor';
@Injectable({
providedIn: 'root'
})
export class VisitorService {
constructor(public http: HttpService) {
}
public pageList: Page<Visitor>[] = [];
public currentPage: Page<Visitor>;
public dayVisit: number;
public totalVisitCount: number;
private ipLocationList: { ip: string, location: string }[] = [];
getVisitor(pageNum: number, pageSize: number) {
const observable = this.http.get(`/admin/visitor/page?count=${pageSize}&page=${pageNum}&showLocation=false`);
observable.subscribe(data => {
if (data.code === 0) {
this.pageList.unshift(data.result);
this.currentPage = data.result;
}
});
}
getDayVisitor() {
this.http.get('/dayVisitCount').subscribe(data => {
if (data.code === 0) {
this.dayVisit = data.result;
}
});
}
getTotalVisitorCount() {
this.http.get('/visitor/count').subscribe(data => {
this.totalVisitCount = data.result;
});
}
getIp(ip: string) {
const location = this.exist(ip);
if (location) {
return location;
}
const observable = this.http.get(`/ip/${ip}`);
observable.subscribe(data => {
if (data.code === 0) {
this.ipLocationList.unshift(data.result);
}
});
return observable;
}
getLocalIp() {
return this.http.get('/ip');
}
private exist(ip): string {
if (this.ipLocationList.length === 0) {
return null;
}
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < this.ipLocationList.length; i++) {
if (this.ipLocationList[i].ip === ip) {
return this.ipLocationList[i].location;
}
}
}
}

View File

@@ -1,42 +0,0 @@
import {Page} from '../classes/page';
import {Observable, of} from 'rxjs';
/**
* 判断 一个Page<any>[] 中是否存在一条已查询的数据
* @param pageNum 页码
* @param pageSize 单页数量
* @param pageList 源数据
* @return 未查到null 查到:该条数据
*/
export function exist<T>(pageNum: number, pageSize: number, pageList: Page<any>[]): Observable<Page<T>> {
if (pageList === undefined || pageList == null || pageList.length === 0) {
return null;
}
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < pageList.length; i++) {
// tslint:disable-next-line:triple-equals
if (pageList[i].pageNum == pageNum && pageList[i].pageSize == pageSize) {
return of<Page<T>>(pageList[i]);
}
}
return null;
}
/**
* 将reqBody对象 转化为拼接到url上面的字符串
* @param reqBody 请求体 from {a:xx,b:xxx}
* @return 字符串 to ==> a=xx&b=xxx
*/
export function reqBody2Str(reqBody: object): string {
let submitBody = '';
for (const key in reqBody) {
// 跳过值为null的参数请求
if (reqBody[key] == null || reqBody[key] === 'null') {
continue;
}
submitBody = submitBody + '&' + key + '=' + reqBody[key];
}
return submitBody.substring(1);
}

View File

@@ -1,15 +0,0 @@
<!doctype html>
<html lang="zh">
<head>
<meta charset="utf-8">
<title>小海博客|后台管理</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="https://56462271.oss-cn-beijing.aliyuncs.com/web/logo.png">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@@ -1,12 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

View File

@@ -1,72 +0,0 @@
/* You can add global styles to this file, and also import other style files */
* {
font-family: Arial, Helvetica, sans-serif;
margin: 0;
padding: 0;
}
body {
background: #F0F1F5;
}
a {
color: #797979;
}
li {
list-style: none;
}
hr {
opacity: 0.4;
}
/* 水平滚动 */
.scrollable-horizontal {
overflow-x: auto;
}
/* 表格 */
table {
margin: 10px 0;
border-collapse: collapse;
width: 100%;
}
tr {
border: 0;
}
tr:nth-child(2n) {
background-color: #f7f7f7;
border: none;
}
tr:hover {
background: #E3F6FF;
}
tr td:last-child{
text-align: center;
}
th {
border: 1px solid #ddd;
background: #EFF3F5;
padding: 8px;
text-align: center;
}
td {
border: 1px solid #ddd;
padding: 10px 8px;
white-space: nowrap;
}
select {
height: 32px;
background: #fafafa;
border: 1px solid #ddd;
border-radius: 4px;
padding: 5px 10px;
margin: 5px 10px;
}

View File

@@ -1,18 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@@ -1,92 +0,0 @@
{
"extends": "tslint:recommended",
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warn"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"import-blacklist": [
true,
"rxjs/Rx"
],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
false,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-use-before-declare": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [
true,
"single"
],
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": [
"codelyzer"
]
}

View File

@@ -5,7 +5,11 @@
"projects": {
"index": {
"projectType": "application",
"schematics": {},
"schematics": {
"@schematics/angular:component": {
"style": "less"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
@@ -26,22 +30,22 @@
"glob": "**/*",
"input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
"output": "/assets/"
}
},
"src/manifest.webmanifest"
],
"styles": [
"src/assets/editor.md/css/editormd.css",
"src/assets/editor/css/editormd.css",
"./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
"src/styles.css"
"src/styles.less"
],
"scripts": [
"src/assets/editor.md/lib/marked.min.js",
"src/assets/editor.md/lib/prettify.min.js",
"src/assets/editor.md/lib/raphael.min.js",
"src/assets/editor.md/lib/underscore.min.js",
"src/assets/editor.md/lib/sequence-diagram.min.js",
"src/assets/editor.md/lib/flowchart.min.js",
"src/assets/editor.md/lib/jquery.flowchart.min.js",
"src/assets/editor.md/editormd.min.js"
"./node_modules/jquery/dist/jquery.min.js",
"src/assets/editor/lib/marked.min.js",
"src/assets/editor/lib/prettify.min.js",
"src/assets/editor/lib/underscore.min.js",
"src/assets/editor/lib/flowchart.min.js",
"src/assets/editor/lib/jquery.flowchart.min.js",
"src/assets/editor/editormd.min.js"
]
},
"configurations": {
@@ -66,8 +70,15 @@
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
],
"serviceWorker": true,
"ngswConfigPath": "ngsw-config.json"
}
}
},
@@ -97,11 +108,12 @@
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
"src/assets",
"src/manifest.webmanifest"
],
"styles": [
"./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
"src/styles.css"
"src/styles.less"
],
"scripts": []
}
@@ -134,5 +146,8 @@
}
}
},
"defaultProject": "index"
"defaultProject": "index",
"cli": {
"analytics": false
}
}

40
build.sh Normal file
View File

@@ -0,0 +1,40 @@
#!/bin/sh
basePath=$(pwd)
$(hash node 2>/dev/null)
if ! [ $? ]; then
echo -e "\t\t请先安装nodejs -------> https://nodejs.org/"
exit 1
else
echo -e "\t\t nodejs\t\t $(node --version)"
fi
$(hash npm 2>/dev/null)
if ! [ $? ]; then
echo -e "\t\t Can't find command npm"
exit 1
else
echo -e "\t\t npm\t\t $(npm --version)"
fi
$(hash ng 2>/dev/null)
if ! [ $? ]; then
echo -e "\t\tinstall angular cli to build the project"
npm install -g @angular/cli
else
echo -e "\t\t angular-cli\t\t $(ng --version)"
fi
# index
echo -e "\t\tBuild for index page "
npm install && ng build --prod
cd ./dist/index/ && tar -cf index.tar ./* && cp index.tar $basePath
#cd "$basePath"
## admin
#echo -e "\t\tBuild for admin page "
#cd $basePath/admin && npm install && ng build --prod
#cd ./dist/admin/ && sed '6s/\"\/\"/\"\/admin\/\"/g' index.html > index.txt && cp index.txt index.html
# cd .. && tar -cf admin.tar ./admin/ && cp admin.tar $basePath

33
e2e/protractor.conf.js Normal file
View File

@@ -0,0 +1,33 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const {SpecReporter} = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function () {
}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({spec: {displayStacktrace: true}}));
}
};

23
e2e/src/app.e2e-spec.ts Normal file
View File

@@ -0,0 +1,23 @@
import {AppPage} from './app.po';
import {browser, logging} from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('index app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

11
e2e/src/app.po.ts Normal file
View File

@@ -0,0 +1,11 @@
import {browser, by, element} from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root .content span')).getText() as Promise<string>;
}
}

View File

@@ -1,32 +0,0 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

Some files were not shown because too many files have changed in this diff Show More