├── .gitignore
├── README.md
├── angular_programming.jpg
├── backend
├── app.js
└── package.json
└── frontend
├── .docker
├── angular-seed.development.dockerfile
├── angular-seed.production.dockerfile
└── nginx.conf
├── .dockerignore
├── .editorconfig
├── .github
├── CONTRIBUTING.md
└── ISSUE_TEMPLATE.md
├── .gitignore
├── .travis.yml
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── LICENSE
├── appveyor.yml
├── docker-compose.production.yml
├── docker-compose.yml
├── gulpfile.ts
├── karma.conf.js
├── package.json
├── protractor.conf.js
├── readme.md
├── src
├── client
│ ├── app
│ │ ├── about
│ │ │ ├── about.component.css
│ │ │ ├── about.component.html
│ │ │ ├── about.component.spec.ts
│ │ │ ├── about.component.ts
│ │ │ ├── about.module.ts
│ │ │ ├── about.routes.ts
│ │ │ └── index.ts
│ │ ├── admin
│ │ │ ├── admin-routing.module.ts
│ │ │ ├── admin.component.html
│ │ │ ├── admin.component.ts
│ │ │ ├── admin.module.ts
│ │ │ └── index.ts
│ │ ├── app-routing.module.ts
│ │ ├── app.component.html
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── center
│ │ │ ├── center.component.css
│ │ │ ├── center.component.html
│ │ │ ├── center.component.ts
│ │ │ ├── center.module.ts
│ │ │ ├── center.routes.ts
│ │ │ ├── index.ts
│ │ │ └── shared
│ │ │ │ ├── center-shared.module.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── questionnaire-controls
│ │ │ │ ├── index.ts
│ │ │ │ ├── questionnaire-controls.component.css
│ │ │ │ ├── questionnaire-controls.component.html
│ │ │ │ └── questionnaire-controls.component.ts
│ │ │ │ ├── questionnaire-detail
│ │ │ │ ├── index.ts
│ │ │ │ ├── questionnaire-detail.component.html
│ │ │ │ └── questionnaire-detail.component.ts
│ │ │ │ └── questionnaire-item
│ │ │ │ ├── index.ts
│ │ │ │ ├── questionnaire-item.component.html
│ │ │ │ └── questionnaire-item.component.ts
│ │ ├── core
│ │ │ ├── core.module.ts
│ │ │ ├── index.ts
│ │ │ ├── navbar
│ │ │ │ ├── index.ts
│ │ │ │ ├── navbar.component.css
│ │ │ │ ├── navbar.component.html
│ │ │ │ └── navbar.component.ts
│ │ │ └── services
│ │ │ │ ├── auth-guard.service.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── login.service.ts
│ │ │ │ ├── questionnaire.service.ts
│ │ │ │ ├── register.service.ts
│ │ │ │ └── user.service.ts
│ │ ├── edit
│ │ │ ├── edit.component.css
│ │ │ ├── edit.component.html
│ │ │ ├── edit.component.ts
│ │ │ ├── edit.module.ts
│ │ │ ├── edit.routes.ts
│ │ │ ├── index.ts
│ │ │ └── shared
│ │ │ │ ├── edit-shared.module.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── question-select
│ │ │ │ ├── index.ts
│ │ │ │ ├── question-select.component.css
│ │ │ │ ├── question-select.component.html
│ │ │ │ └── question-select.component.ts
│ │ │ │ └── questionnaire-outline
│ │ │ │ ├── index.ts
│ │ │ │ ├── questionnaire-outline.component.css
│ │ │ │ ├── questionnaire-outline.component.html
│ │ │ │ └── questionnaire-outline.component.ts
│ │ ├── home
│ │ │ ├── home.component.css
│ │ │ ├── home.component.e2e-spec.ts
│ │ │ ├── home.component.html
│ │ │ ├── home.component.nonspec.ts
│ │ │ ├── home.component.ts
│ │ │ ├── home.module.ts
│ │ │ ├── home.routes.ts
│ │ │ └── index.ts
│ │ ├── main-prod.ts
│ │ ├── main.ts
│ │ ├── published
│ │ │ ├── index.ts
│ │ │ ├── published-routing.module.ts
│ │ │ ├── published.component.css
│ │ │ ├── published.component.html
│ │ │ ├── published.component.ts
│ │ │ └── published.module.ts
│ │ ├── shared
│ │ │ ├── config
│ │ │ │ └── env.config.ts
│ │ │ ├── index.ts
│ │ │ ├── models
│ │ │ │ ├── index.ts
│ │ │ │ ├── question.model.ts
│ │ │ │ ├── questionnaire.model.ts
│ │ │ │ └── user.model.ts
│ │ │ ├── question
│ │ │ │ ├── index.ts
│ │ │ │ ├── question.component.ts
│ │ │ │ ├── question.module.ts
│ │ │ │ └── shared
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── question-checkbox
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── question-checkbox.component.html
│ │ │ │ │ └── question-checkbox.component.ts
│ │ │ │ │ ├── question-radio
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── question-radio.component.html
│ │ │ │ │ └── question-radio.component.ts
│ │ │ │ │ ├── question-score
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── question-score.component.html
│ │ │ │ │ └── question-score.component.ts
│ │ │ │ │ ├── question-shared.module.ts
│ │ │ │ │ └── question-text
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── question-text.component.html
│ │ │ │ │ └── question-text.component.ts
│ │ │ ├── questionnaire
│ │ │ │ ├── index.ts
│ │ │ │ ├── questionnaire.component.css
│ │ │ │ ├── questionnaire.component.html
│ │ │ │ ├── questionnaire.component.ts
│ │ │ │ └── questionnaire.module.ts
│ │ │ └── shared.module.ts
│ │ ├── system-config.ts
│ │ └── user
│ │ │ ├── index.ts
│ │ │ ├── shared
│ │ │ ├── field
│ │ │ │ ├── field-base.ts
│ │ │ │ ├── field-radio.ts
│ │ │ │ ├── field-select.ts
│ │ │ │ ├── field-text.ts
│ │ │ │ ├── field-validators.ts
│ │ │ │ ├── field.component.css
│ │ │ │ ├── field.component.html
│ │ │ │ ├── field.component.ts
│ │ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ ├── login
│ │ │ │ ├── index.ts
│ │ │ │ ├── login.component.css
│ │ │ │ ├── login.component.html
│ │ │ │ └── login.component.ts
│ │ │ ├── register
│ │ │ │ ├── index.ts
│ │ │ │ ├── register.component.css
│ │ │ │ ├── register.component.html
│ │ │ │ └── register.component.ts
│ │ │ └── user-shared.module.ts
│ │ │ ├── user.module.ts
│ │ │ └── user.routes.ts
│ ├── assets
│ │ ├── data.json
│ │ ├── img
│ │ │ ├── about
│ │ │ │ ├── create.jpg
│ │ │ │ ├── faq.jpg
│ │ │ │ └── my.jpg
│ │ │ ├── edit
│ │ │ │ ├── checkbox.png
│ │ │ │ ├── radio.png
│ │ │ │ ├── star.png
│ │ │ │ └── text.png
│ │ │ ├── home
│ │ │ │ ├── banner_01.jpg
│ │ │ │ ├── banner_02.jpg
│ │ │ │ └── banner_03.jpg
│ │ │ └── notfound.png
│ │ └── svg
│ │ │ └── more.svg
│ ├── css
│ │ ├── fonts
│ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ ├── glyphicons-halflings-regular.svg
│ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ └── glyphicons-halflings-regular.woff2
│ │ ├── libs
│ │ │ └── bootstrap.min.css
│ │ └── main.css
│ ├── index.html
│ └── tsconfig.json
└── e2e
│ ├── specs
│ └── about.component.e2e-spec.ts
│ └── tsconfig.json
├── test-config.js
├── test-main.js
├── tools
├── .gitignore
├── README.md
├── config.ts
├── config
│ ├── banner-256.txt
│ ├── banner.txt
│ ├── project.config.ts
│ ├── seed.config.interfaces.ts
│ ├── seed.config.ts
│ └── seed.tslint.json
├── debug.ts
├── env
│ ├── base.ts
│ ├── dev.ts
│ ├── env-config.interface.ts
│ └── prod.ts
├── manual_typings
│ ├── project
│ │ └── sample.package.d.ts
│ └── seed
│ │ ├── autoprefixer.d.ts
│ │ ├── cssnano.d.ts
│ │ ├── express-history-api-fallback.d.ts
│ │ ├── istream.d.ts
│ │ ├── karma.d.ts
│ │ ├── merge-stream.d.ts
│ │ ├── open.d.ts
│ │ ├── operators.d.ts
│ │ ├── slash.d.ts
│ │ ├── systemjs-builder.d.ts
│ │ └── tildify.d.ts
├── tasks
│ ├── assets_task.ts
│ ├── css_task.ts
│ ├── project
│ │ └── sample.task.ts
│ ├── seed
│ │ ├── build.assets.dev.ts
│ │ ├── build.assets.prod.ts
│ │ ├── build.bundle.rxjs.ts
│ │ ├── build.bundles.app.exp.ts
│ │ ├── build.bundles.app.ts
│ │ ├── build.bundles.ts
│ │ ├── build.docs.ts
│ │ ├── build.html_css.ts
│ │ ├── build.index.dev.ts
│ │ ├── build.index.prod.ts
│ │ ├── build.js.dev.ts
│ │ ├── build.js.e2e.ts
│ │ ├── build.js.prod.exp.ts
│ │ ├── build.js.prod.ts
│ │ ├── build.js.test.ts
│ │ ├── build.tools.ts
│ │ ├── check.tools.ts
│ │ ├── check.versions.ts
│ │ ├── clean.all.ts
│ │ ├── clean.coverage.ts
│ │ ├── clean.dev.ts
│ │ ├── clean.e2e.ts
│ │ ├── clean.prod.ts
│ │ ├── clean.tools.ts
│ │ ├── clear.files.ts
│ │ ├── compile.ahead.prod.ts
│ │ ├── copy.prod.ts
│ │ ├── e2e.ts
│ │ ├── generate.manifest.ts
│ │ ├── karma.run.ts
│ │ ├── karma.run.with_coverage.ts
│ │ ├── karma.watch.ts
│ │ ├── minify.bundles.ts
│ │ ├── print.banner.ts
│ │ ├── serve.coverage.ts
│ │ ├── serve.coverage.watch.ts
│ │ ├── serve.docs.ts
│ │ ├── server.prod.ts
│ │ ├── server.start.ts
│ │ ├── start.deving.ts
│ │ ├── tslint.ts
│ │ ├── watch.dev.ts
│ │ ├── watch.e2e.ts
│ │ ├── watch.test.ts
│ │ └── webdriver.ts
│ ├── task.ts
│ └── typescript_task.ts
├── utils.ts
└── utils
│ ├── project.utils.ts
│ ├── project
│ └── sample_util.ts
│ ├── seed.utils.ts
│ └── seed
│ ├── clean.ts
│ ├── code_change_tools.ts
│ ├── karma.start.ts
│ ├── server.ts
│ ├── tasks_tools.ts
│ ├── template_locals.ts
│ ├── tsproject.ts
│ └── watch.ts
├── tsconfig.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 | **/*.log
2 | node_modules
3 | dist
4 | tmp
5 | .DS_Store
6 | .idea
7 | _*
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular 2 调查问卷示例项目
2 |
3 | ### 注意:新版本(Angular 4 + Angular CLI)已迁移至新repo
4 |
5 | https://github.com/angular-programming/angular-questionnaire
6 |
7 | ### 这是由广发证券互联网金融技术团队出品的原创书籍《揭秘 Angular 2》的第三部分项目实战的源代码。
8 |
9 | 
10 |
11 | 这个示例项目包含以下特点:
12 |
13 | * 遵循官方最佳实践的目录布局
14 | * 代码难易程度适中,方便学习
15 | * 功能丰富的脚手架,易于扩展使用
16 | * 简洁化的后端服务,聚焦前端框架学习
17 |
18 |
19 | ## 如何上手
20 |
21 | 调查问卷项目包括前端 frontend 目录以及后端 backend 目录。我们可以先运行后端服务,方便前端的注册与登录用户以及提供问卷相关的服务。安装过 Node.js 之后(确保你的 Node.js 版本为 6.x 及以上),在终端运行以下命令:
22 |
23 | ```bash
24 | cd backend
25 | npm install
26 | node app
27 | ```
28 |
29 | 接下来,将终端目录定位 frontend 之中,再运行以下命令:
30 |
31 | ```bash
32 | npm install
33 | npm start
34 | ```
35 |
36 | 以上前后台的命令都执行完后,即成功启动整个项目应用。
37 |
--------------------------------------------------------------------------------
/angular_programming.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angular-programming/angular2-questionnaire/c000597bda2635482c5876efb5805fba8a5cae68/angular_programming.jpg
--------------------------------------------------------------------------------
/backend/app.js:
--------------------------------------------------------------------------------
1 | const jsonServer = require('json-server')
2 | const uuid = require('node-uuid');
3 | const bodyParser = require('body-parser')
4 | const low = require('lowdb')
5 | const storage = require('lowdb/file-async')
6 |
7 | // import jsonServer from 'json-server';
8 | // import uuid from 'node-uuid';
9 | // import bodyParser from 'body-parser';
10 |
11 | // import low from 'lowdb';
12 | // import storage from 'lowdb/file-async';
13 |
14 | var crypto = require('crypto');
15 |
16 | //创建一个Express服务器
17 | const server = jsonServer.create();
18 |
19 | //使用json-server默认选择的中间件(logger,static, cors和no-cache)
20 | server.use(jsonServer.defaults());
21 |
22 | //使用body-parser中间件
23 | server.use(bodyParser.json());
24 |
25 |
26 | //数据文件
27 | const dbfile = process.env.prod === '1' ? 'db.json' : '_db.json';
28 |
29 | //创建一个lowdb实例
30 | const db = low(dbfile, {storage});
31 |
32 |
33 |
34 | var md5 = function(str) {
35 | return crypto
36 | .createHash('md5')
37 | .update(str.toString())
38 | .digest('hex');
39 | };
40 |
41 | //添加新问卷
42 | server.post('/questionnaire/add', (req, res) => {
43 | const item = req.body;
44 | item.id = uuid.v1();
45 | item.createDate = new Date().toLocaleDateString();
46 | db('questionnaires').push(item).then(() => {
47 | res.json({'success':true, data:item});
48 | });
49 | });
50 |
51 | //删除已有问卷
52 | server.get('/questionnaire/delete/:id', (req, res)=>{
53 | db('questionnaires').remove({id: req.params.id}).then(()=>{
54 | res.json({'success': true});
55 | });
56 | });
57 |
58 | //获取所有问卷
59 | server.get('/questionnaires', (req, res) => {
60 | const questionnaires = db('questionnaires');
61 | res.json({'success':true, data:questionnaires});
62 | });
63 |
64 | //根据id获取问卷数据
65 | server.get('/questionnaire/:id', (req, res) => {
66 | const questionnaire = db('questionnaires').find({id: req.params.id});
67 | res.json({'success':true, data:questionnaire});
68 | });
69 |
70 | //更新已有问卷
71 | server.post('/questionnaire/update', (req, res) => {
72 | const item = req.body;
73 | db('questionnaires').chain().find({id:item.id}).assign(item).value();
74 | res.json({'success':true, data:item});
75 | });
76 |
77 | //发布问卷
78 | server.get('/questionnaire/publish/:id', (req, res)=>{
79 | const item = db('questionnaires').chain().find({id:req.params.id});
80 | item.assign({state:1}).value();
81 | res.json({'success':true, data:item});
82 | });
83 |
84 | // get userinfo
85 | server.get('/user/:username', function(req, res) {
86 | var user = db('user')
87 | .find({
88 | username: req.params.username
89 | });
90 |
91 | res.json({
92 | success: true,
93 | data: {
94 | username: user.username,
95 | createDate: user.createDate
96 | }
97 | });
98 | });
99 |
100 | // register
101 | server.post('/user/add', function(req, res) {
102 | var item = req.body;
103 | var user = db('user')
104 | .find({
105 | username: item.username
106 | });
107 | if (user) {
108 | res.json({
109 | success: false,
110 | message: '"' + item.username + '" is exists'
111 | })
112 | } else {
113 | item.password = md5(item.password);
114 | item.createDate = new Date().toLocaleDateString();
115 | db('user')
116 | .push(item)
117 | .then(function() {
118 | res.json({
119 | success: true
120 | });
121 | });
122 | }
123 | });
124 |
125 | // login
126 | server.post('/login', function(req, res) {
127 | var data = req.body || {};
128 | var username = data.username;
129 | var user = db('user')
130 | .find({
131 | username: username
132 | });
133 |
134 | if (user && user.password === md5(data.password)) {
135 | // todo reset session
136 | res.json({
137 | success: true
138 | });
139 | } else {
140 | res.json({
141 | success: false,
142 | message: 'username or password error'
143 | });
144 | }
145 | });
146 |
147 | //路由配置
148 | const router = jsonServer.router(dbfile);
149 | server.use('/api', router);
150 |
151 | //启动服务,并监听5000端口
152 | server.listen(5000, () => {
153 | console.log('server is running at ', 5000, dbfile);
154 | });
155 |
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "body-parser": "^1.14.2",
13 | "json-server": "^0.8.7",
14 | "lowdb": "^0.12.2",
15 | "node-uuid": "^1.4.7"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/frontend/.docker/angular-seed.development.dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:6.6
2 |
3 | # prepare a user which runs everything locally! - required in child images!
4 | RUN useradd --user-group --create-home --shell /bin/false app
5 |
6 | ENV HOME=/home/app
7 | WORKDIR $HOME
8 |
9 | ENV APP_NAME=angular-seed
10 |
11 | # before switching to user we need to set permission properly
12 | # copy all files, except the ignored files from .dockerignore
13 | COPY . $HOME/$APP_NAME/
14 | RUN chown -R app:app $HOME/*
15 |
16 | USER app
17 | WORKDIR $HOME/$APP_NAME
18 |
19 | RUN npm install
20 |
--------------------------------------------------------------------------------
/frontend/.docker/angular-seed.production.dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:6.6
2 |
3 | # prepare a user which runs everything locally! - required in child images!
4 | RUN useradd --user-group --create-home --shell /bin/false app
5 |
6 | ENV HOME=/home/app
7 | WORKDIR $HOME
8 |
9 | ENV APP_NAME=angular-seed
10 |
11 | # before switching to user we need to set permission properly
12 | # copy all files, except the ignored files from .dockerignore
13 | COPY . $HOME/$APP_NAME/
14 | RUN chown -R app:app $HOME/*
15 |
16 | USER app
17 | WORKDIR $HOME/$APP_NAME
18 |
19 | RUN npm install
20 |
--------------------------------------------------------------------------------
/frontend/.docker/nginx.conf:
--------------------------------------------------------------------------------
1 | map $http_upgrade $connection_upgrade {
2 | default upgrade;
3 | '' close;
4 | }
5 |
6 | server {
7 |
8 | listen ${NGINX_PORT};
9 |
10 | server_name ${NGINX_HOST};
11 | # ssl on;
12 | # ssl_certificate angular-seed_server.crt;
13 | # ssl_certificate_key angular-seed_server.pem;
14 | #
15 | # ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
16 | # ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
17 | # ssl_prefer_server_ciphers on;
18 | # ssl_session_cache shared:SSL:10m;
19 | # ssl_session_tickets off; # Requires nginx >= 1.5.9
20 | # add_header Strict-Transport-Security "max-age=63072000; preload";
21 | # add_header X-Frame-Options DENY;
22 | #
23 | # location /api/microservice1 {
24 | # rewrite ^/api/microservice1/(.*)$ /$1 break;
25 | # proxy_pass https://microservice1/;
26 | # proxy_http_version 1.1;
27 | # proxy_set_header X-Forwarded-For $remote_addr;
28 | # }
29 |
30 | location / {
31 | root /var/www/dist/prod;
32 | try_files $uri /index.html;
33 | index index.html;
34 | gzip on;
35 | gzip_types text/css text/javascript application/x-javascript application/json;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/frontend/.dockerignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | dist
3 | tmp
4 |
5 | # dependencies
6 | node_modules
7 | bower_components
8 |
9 | # IDEs and editors
10 | .idea
11 | .vscode
12 | .project
13 | .classpath
14 | *.launch
15 | .settings/
16 |
17 | # misc
18 | .sass-cache
19 | connect.lock
20 | coverage/*
21 | libpeerconnection.log
22 | npm-debug.log
23 | testem.log
24 | typings
25 |
26 | # e2e
27 | e2e/*.js
28 | e2e/*.map
29 |
30 | #System Files
31 | .DS_Store
32 | Thumbs.db
33 |
--------------------------------------------------------------------------------
/frontend/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 2
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | insert_final_newline = false
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/frontend/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Submitting Pull Requests
2 |
3 | **Please follow these basic steps to simplify pull request reviews - if you don't you'll probably just be asked to anyway.**
4 |
5 | * Please rebase your branch against the current master
6 | * Run ```npm install``` to make sure your development dependencies are up-to-date
7 | * Please ensure that the test suite passes **and** that code is lint free before submitting a PR by running:
8 | * ```npm test```
9 | * If you've added new functionality, **please** include tests which validate its behaviour
10 | * Make reference to possible [issues](https://github.com/mgechev/angular2-seed/issues) on PR comment
11 |
12 | ## Submitting bug reports
13 |
14 | * Please detail the affected browser(s) and operating system(s)
15 | * Please be sure to state which version of node **and** npm you're using
16 |
--------------------------------------------------------------------------------
/frontend/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | **I'm submitting a ...** (check one with "x")
6 | ```
7 | [ ] bug report => search github for a similar issue or PR before submitting
8 | [ ] feature request
9 | [ ] support request => Please do not submit support request here, instead see use [gitter](https://gitter.im/mgechev/angular2-seed) or [stackoverflow](https://stackoverflow.com/questions/tagged/angular2)
10 | ```
11 |
12 | **Current behavior**
13 |
14 |
15 | **Expected behavior**
16 |
17 |
18 | **Minimal reproduction of the problem with instructions**
19 |
23 |
24 | **What is the motivation / use case for changing the behavior?**
25 |
26 |
27 | **Please tell us about your environment:**
28 |
29 |
30 | * **Angular Seed Version:** `aaaaf75`
31 |
32 |
33 | * **Node:** `node --version` =
34 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 | coverage_js
16 |
17 | # ignore documentation
18 | documentation
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | # Commenting this out is preferred by some people, see
28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
29 | node_modules
30 | typings
31 | ts-node
32 |
33 | # Users Environment Variables
34 | .lock-wscript
35 | .tsdrc
36 | .typingsrc
37 |
38 | #IDE configuration files
39 | .idea
40 | *.iml
41 |
42 | /tools/**/*.js
43 | dist
44 | dev
45 | docs
46 | lib
47 | test
48 | tmp
49 |
50 | gulpfile.js
51 | gulpfile.js.map
52 |
53 | # OS X trash files
54 | .DS_Store
55 | src/client/app/shared/config/env.config.js
56 | src/client/app/shared/config/env.config.js.map
57 |
58 | npm-*
59 |
--------------------------------------------------------------------------------
/frontend/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js: stable
3 |
4 | sudo: false
5 |
6 | install: true # yarn bug
7 |
8 | addons:
9 | firefox: "45.0"
10 |
11 | os:
12 | - linux
13 | - osx
14 |
15 | before_install:
16 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
17 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated xctool || brew upgrade xctool; fi
18 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CHROME_BIN=chromium-browser; fi # Karma CI
19 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew cask install google-chrome; fi # Karma CI
20 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export DISPLAY=:99.0; fi
21 |
22 | before_script:
23 | - npm install
24 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sh -e /etc/init.d/xvfb start; fi
25 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then nohup bash -c "webdriver-manager start 2>&1 &"; fi # Protractor CI
26 |
27 | after_failure:
28 | - cat /home/travis/build/mgechev/angular-seed/npm-debug.log
29 |
30 | branches:
31 | only: master
32 |
33 | notifications:
34 | email: true
35 | webhooks:
36 | urls: https://webhooks.gitter.im/e/565e4b2fed3b96c1b964
37 | on_success: change # options: [always|never|change] default: always
38 | on_failure: always # options: [always|never|change] default: always
39 | on_start: never # options: [always|never|change] default: always
40 |
41 | cache:
42 | directories: node_modules
43 |
44 | script:
45 | - npm run tests.all && npm run build.prod.exp
46 |
--------------------------------------------------------------------------------
/frontend/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Launch Chrome against localhost, with sourcemaps",
6 | "type": "chrome",
7 | "preLaunchTask": "start",
8 | "request": "launch",
9 | "url": "http://localhost:5555",
10 | "sourceMaps": true,
11 | "webRoot": "${workspaceRoot}/src/client",
12 | "sourceMapPathOverrides": {
13 | "app/*": "${webRoot}/app/*"
14 | }
15 | },
16 | {
17 | "name": "Attach to Chrome, with sourcemaps",
18 | "type": "chrome",
19 | "request": "attach",
20 | "port": 9222,
21 | "sourceMaps": true,
22 | "webRoot": "${workspaceRoot}/src/client",
23 | "sourceMapPathOverrides": {
24 | "app/*": "${webRoot}/app/*"
25 | }
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "typescript.tsdk": "node_modules/typescript/lib",
4 | "search.exclude": {
5 | "**/coverage_js": true,
6 | "**/coverage": true,
7 | "**/dist": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.1.0",
3 | "command": "npm",
4 | "isShellCommand": true,
5 | "showOutput": "always",
6 | "suppressTaskName": true,
7 | "tasks": [
8 | {
9 | "taskName": "tslint",
10 | "args": [ "run", "gulp", "tslint", "--color" ],
11 | "problemMatcher": {
12 | "owner": "tslint",
13 | "fileLocation": [
14 | "relative",
15 | "${workspaceRoot}"
16 | ],
17 | "severity": "warning",
18 | "pattern": {
19 | "regexp": "^(\\S.*)\\[(\\d+), (\\d+)\\]:\\s+(.*)$",
20 | "file": 1,
21 | "line": 2,
22 | "column": 3,
23 | "message": 4
24 | }
25 | }
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/frontend/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Minko Gechev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/frontend/appveyor.yml:
--------------------------------------------------------------------------------
1 | # AppVeyor file
2 | # http://www.appveyor.com/docs/appveyor-yml
3 | # This file: cloned from https://github.com/gruntjs/grunt/blob/master/appveyor.yml
4 |
5 | # Build version format
6 | version: "{build}"
7 |
8 | # Test against this version of Node.js
9 | environment:
10 | nodejs_version: "Stable"
11 |
12 | build: off
13 |
14 | clone_depth: 10
15 |
16 | # Fix line endings on Windows
17 | init:
18 | - git config --global core.autocrlf true
19 |
20 | install:
21 | - ps: Install-Product node $env:nodejs_version
22 | - npm install -g npm@3.10.8
23 | - ps: $env:path = $env:appdata + "\npm;" + $env:path
24 | - npm install && npm install karma-ie-launcher
25 |
26 | test_script:
27 | # Output useful info for debugging.
28 | - node --version && npm --version
29 | # We test multiple Windows shells because of prior stdout buffering issues
30 | # filed against Grunt. https://github.com/joyent/node/issues/3584
31 | - ps: "npm --version # PowerShell" # Pass comment to PS for easier debugging
32 | - npm run tests.all && npm run build.prod.exp
33 |
34 | notifications:
35 | - provider: Webhook
36 | url: https://webhooks.gitter.im/e/cfd8ce5ddee6f3a0b0c9
37 | on_build_success: false
38 | on_build_failure: true
39 | on_build_status_changed: true
40 |
41 | cache: node_modules -> package.json
42 |
--------------------------------------------------------------------------------
/frontend/docker-compose.production.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 |
5 | angular-seed:
6 | build:
7 | context: .
8 | dockerfile: ./.docker/angular-seed.production.dockerfile
9 | command: npm run build.prod
10 | container_name: angular-seed-build-prod
11 | image: angular-seed
12 | networks:
13 | - prod-network
14 | volumes:
15 | - ./dist:/home/app/angular-seed/dist
16 |
17 | angular-seed-nginx:
18 | command: /bin/bash -c "envsubst '$$NGINX_HOST $$NGINX_PORT' < /etc/nginx/conf.d/angular-seed.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
19 | container_name: angular-seed-nginx-prod
20 | environment:
21 | - NGINX_HOST=localhost
22 | - NGINX_PORT=80
23 | image: nginx
24 | networks:
25 | - prod-network
26 | ports:
27 | - '5555:80'
28 | volumes:
29 | - ./.docker/nginx.conf:/etc/nginx/conf.d/angular-seed.template
30 | - ./dist/prod:/var/www/dist/prod
31 |
32 | networks:
33 | prod-network:
34 | driver: bridge
35 |
--------------------------------------------------------------------------------
/frontend/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 |
5 | angular-seed:
6 | build:
7 | context: .
8 | dockerfile: ./.docker/angular-seed.development.dockerfile
9 | command: npm start
10 | container_name: angular-seed-start
11 | image: angular-seed
12 | networks:
13 | - dev-network
14 | ports:
15 | - '5555:5555'
16 |
17 | networks:
18 | dev-network:
19 | driver: bridge
20 |
--------------------------------------------------------------------------------
/frontend/protractor.conf.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | baseUrl: 'http://localhost:5555/',
3 |
4 | specs: [
5 | './dist/e2e/**/*.e2e-spec.js'
6 | ],
7 |
8 | exclude: [],
9 |
10 | // 'jasmine' by default will use the latest jasmine framework
11 | framework: 'jasmine',
12 |
13 | // allScriptsTimeout: 110000,
14 |
15 | jasmineNodeOpts: {
16 | // showTiming: true,
17 | showColors: true,
18 | isVerbose: false,
19 | includeStackTrace: false,
20 | // defaultTimeoutInterval: 400000
21 | },
22 |
23 | directConnect: true,
24 |
25 | capabilities: {
26 | browserName: 'chrome'
27 | },
28 |
29 | onPrepare: function() {
30 | browser.ignoreSynchronization = false;
31 | },
32 |
33 |
34 | /**
35 | * Angular 2 configuration
36 | *
37 | * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching
38 | * `rootEl`
39 | */
40 | useAllAngular2AppRoots: true
41 | };
42 |
43 | if (process.env.TRAVIS) {
44 | config.capabilities = {
45 | browserName: 'firefox'
46 | };
47 | }
48 |
49 | exports.config = config;
50 |
--------------------------------------------------------------------------------
/frontend/readme.md:
--------------------------------------------------------------------------------
1 | # TODO
2 | *.
--------------------------------------------------------------------------------
/frontend/src/client/app/about/about.component.css:
--------------------------------------------------------------------------------
1 | :host {
2 | display: block;
3 | padding: 16px;
4 | }
5 |
6 | accordion-group .col-lg-3 {
7 | text-align: center;
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/src/client/app/about/about.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
![{{item.text}}]()
8 |
9 |
10 | {{ item?.desc }}
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/frontend/src/client/app/about/about.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 | import { By } from '@angular/platform-browser';
3 |
4 | import { DebugElement } from '@angular/core';
5 |
6 | import { AboutComponent } from './about.component';
7 | import { AccordionModule } from 'ng2-bootstrap/components/accordion';
8 | import { SharedModule } from '../shared/shared.module';
9 |
10 | export function main() {
11 | describe('About component', () => {
12 |
13 | let comp: AboutComponent;
14 | let fixture: ComponentFixture;
15 | let aboutEl: DebugElement;
16 |
17 | // setting module for testing
18 | // compile template and css
19 | beforeEach( async(() => {
20 | TestBed.configureTestingModule({
21 | imports: [SharedModule, AccordionModule],
22 | declarations: [AboutComponent],
23 | })
24 | .compileComponents();
25 | }));
26 |
27 | // synchronous beforeEach
28 | beforeEach(() => {
29 | fixture = TestBed.createComponent(AboutComponent);
30 | comp = fixture.componentInstance;
31 | aboutEl = fixture.debugElement;
32 |
33 | fixture.detectChanges(); // trigger initial data binding
34 | });
35 |
36 | it('should render accordion', ()=>{
37 |
38 | const de = aboutEl.queryAll(By.css('accordion'));
39 | expect(de.length).toBe(1);
40 | });
41 |
42 | it('should render correct accordion text', ()=>{
43 |
44 | // const de = aboutEl.queryAll(By.css('accordion'));
45 | expect(aboutEl.nativeElement.textContent).toContain('常见FAQ');
46 | });
47 | });
48 | }
49 |
50 | ////// Test Host Component //////
51 | import { Component } from '@angular/core';
52 |
53 | @Component({
54 | selector: 'test-cmp',
55 | template: ''
56 | })
57 | class TestComponent {}
58 |
--------------------------------------------------------------------------------
/frontend/src/client/app/about/about.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | moduleId: module.id,
5 | selector: 'sd-help',
6 | templateUrl: 'about.component.html',
7 | styleUrls: ['about.component.css']
8 | })
9 | export class AboutComponent {
10 |
11 | private items: any[];
12 |
13 | constructor() {
14 | this.items = [
15 | {
16 | icon:'./assets/img/about/faq.jpg',
17 | text:'常见FAQ',
18 | desc:'使用问卷收集数据的基本流程是:创建问卷、编辑问卷、设置问卷、预览问卷、回收问卷、数据分析等几个步骤。问卷提供三种创建问卷方式,创建空白问卷、选择问卷模板、文本编辑器。可以灵活设置问卷的显示、回收条件,在线实时统计回收结果,并可以导出Excel和SPSS数据。详细操作指引请参照帮助中心”操作指引“部分。',
19 | open: true
20 | },
21 | {
22 | icon:'./assets/img/about/create.jpg',
23 | text:'创建问卷',
24 | desc:'问卷,共有三种创建问卷的方式让您来选择使用:创建空白问卷、选择问卷模板、文本编辑器,可根据自身的使用习惯,选择最合适的方式来快速创建一份问卷。',
25 | open: false
26 | },
27 | {
28 | icon:'./assets/img/about/my.jpg',
29 | text:'我的问卷',
30 | desc:'问卷编辑、预览完成后,一份在线问卷即完成,此时可以通过对问卷的复制、编辑、删除、发布、统计、导出、分享等功能,实现对问卷的全面管理',
31 | open: false
32 | }
33 | ];
34 |
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/src/client/app/about/about.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | import { AboutComponent } from './about.component';
5 | import { AccordionModule } from 'ng2-bootstrap/components/accordion';
6 |
7 | @NgModule({
8 | imports: [CommonModule, AccordionModule],
9 | declarations: [AboutComponent],
10 | exports: [AboutComponent]
11 | })
12 | export class AboutModule { }
13 |
--------------------------------------------------------------------------------
/frontend/src/client/app/about/about.routes.ts:
--------------------------------------------------------------------------------
1 | import { Route } from '@angular/router';
2 |
3 | import { AboutComponent } from './index';
4 |
5 | export const AboutRoutes: Route[] = [
6 | {
7 | path: 'about',
8 | component: AboutComponent
9 | }
10 | ];
11 |
--------------------------------------------------------------------------------
/frontend/src/client/app/about/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This barrel file provides the export for the lazy loaded AboutComponent.
3 | */
4 | export * from './about.component';
5 | export * from './about.routes';
6 |
--------------------------------------------------------------------------------
/frontend/src/client/app/admin/admin-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { AdminComponent } from './index';
5 | import { AboutRoutes } from '../about/index';
6 | import { CenterRoutes } from '../center/index';
7 | import { EditRoutes } from '../edit/index';
8 | import { UserRoutes } from '../user/index';
9 |
10 | const routes: Routes = [
11 | {
12 | path: 'admin',
13 | component: AdminComponent,
14 | children: [
15 | ...AboutRoutes,
16 | ...EditRoutes,
17 | ...UserRoutes,
18 | ...CenterRoutes
19 | ]
20 | }
21 | ];
22 |
23 | @NgModule({
24 | imports: [RouterModule.forChild(routes)],
25 | exports: [RouterModule]
26 | })
27 | export class AdminRoutingModule { }
28 |
29 |
--------------------------------------------------------------------------------
/frontend/src/client/app/admin/admin.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/frontend/src/client/app/admin/admin.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | moduleId: module.id,
5 | selector: 'sd-admin',
6 | templateUrl: 'admin.component.html',
7 | })
8 | export class AdminComponent { }
9 |
--------------------------------------------------------------------------------
/frontend/src/client/app/admin/admin.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | import { AdminComponent } from './admin.component';
5 | import { AdminRoutingModule } from './admin-routing.module';
6 | import { CoreModule } from '../core/core.module';
7 |
8 | @NgModule({
9 | imports: [CommonModule, CoreModule, AdminRoutingModule],
10 | declarations: [AdminComponent],
11 | exports: [AdminComponent]
12 | })
13 | export class AdminModule { }
14 |
--------------------------------------------------------------------------------
/frontend/src/client/app/admin/index.ts:
--------------------------------------------------------------------------------
1 | export * from './admin.component';
2 |
--------------------------------------------------------------------------------
/frontend/src/client/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { HomeRoutes, HomeComponent } from './home/index';
5 |
6 | export const routes: Routes = [
7 | ...HomeRoutes
8 | //{ path: '**', component: HomeComponent }
9 | ];
10 |
11 | @NgModule({
12 | imports: [RouterModule.forRoot(routes)],
13 | exports: [RouterModule]
14 | })
15 | export class AppRoutingModule { }
16 |
--------------------------------------------------------------------------------
/frontend/src/client/app/app.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/client/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { FormsModule } from '@angular/forms';
3 | import { TestBed } from '@angular/core/testing';
4 | import { APP_BASE_HREF } from '@angular/common';
5 |
6 | import {
7 | async
8 | } from '@angular/core/testing';
9 | import {
10 | Route
11 | } from '@angular/router';
12 | import {
13 | RouterTestingModule
14 | } from '@angular/router/testing';
15 | import { AppComponent } from './app.component';
16 | import { HomeComponent } from './home/home.component';
17 | import { AboutComponent } from './about/about.component';
18 | import { ToolbarComponent } from './shared/toolbar/toolbar.component';
19 | import { NavbarComponent } from './core/navbar/navbar.component';
20 |
21 |
22 | export function main() {
23 |
24 | describe('App component', () => {
25 |
26 | it('should just work dead simple', ()=>{
27 | expect(true).toBeTruthy();
28 | });
29 |
30 | /* let config: Route[] = [
31 | { path: '', component: HomeComponent },
32 | { path: 'about', component: AboutComponent }
33 | ];
34 | beforeEach(() => {
35 | TestBed.configureTestingModule({
36 | imports: [FormsModule, RouterTestingModule.withRoutes(config)],
37 | declarations: [TestComponent, ToolbarComponent,
38 | NavbarComponent, AppComponent,
39 | HomeComponent, AboutComponent],
40 | providers: [
41 | { provide: APP_BASE_HREF, useValue: '/' }
42 | ]
43 | });
44 | });
45 |
46 | it('should build without a problem',
47 | async(() => {
48 | TestBed
49 | .compileComponents()
50 | .then(() => {
51 | let fixture = TestBed.createComponent(TestComponent);
52 | let compiled = fixture.nativeElement;
53 |
54 | expect(compiled).toBeTruthy();
55 | });
56 | }));*/
57 | });
58 | }
59 |
60 | @Component({
61 | selector: 'test-cmp',
62 | template: ''
63 | })
64 |
65 | class TestComponent {
66 | }
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/frontend/src/client/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | import { Config } from './shared/index';
4 | import {UserService} from "./core/services/user.service";
5 |
6 | /**
7 | * This class represents the main application component. Within the @Routes annotation is the configuration of the
8 | * applications routes, configuring the paths for the lazy loaded components (HomeComponent, AboutComponent).
9 | */
10 | @Component({
11 | moduleId: module.id,
12 | selector: 'sd-app',
13 | templateUrl: 'app.component.html',
14 | })
15 | export class AppComponent implements OnInit {
16 | constructor(private userService: UserService) {
17 | console.log('Environment config', Config);
18 | }
19 |
20 | ngOnInit():void {
21 | this.userService.getUser().subscribe(); // 获取用户信息
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/frontend/src/client/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { APP_BASE_HREF } from '@angular/common';
4 | import { HttpModule } from '@angular/http';
5 |
6 | import { AppComponent } from './app.component';
7 | import { AppRoutingModule } from './app-routing.module';
8 | import { CoreModule } from './core/core.module';
9 | import { AdminModule } from './admin/admin.module';
10 | import { AboutModule } from './about/about.module';
11 | import { HomeModule } from './home/home.module';
12 | import { EditModule } from './edit/edit.module';
13 | import { CenterModule } from './center/center.module';
14 | import { UserModule } from './user/user.module';
15 | import { PublishedModule } from './published/published.module';
16 |
17 | @NgModule({
18 | imports: [CoreModule, AdminModule, BrowserModule, HttpModule, AppRoutingModule, AboutModule, HomeModule, UserModule,
19 | EditModule, CenterModule, PublishedModule],
20 | declarations: [AppComponent],
21 | providers: [{
22 | provide: APP_BASE_HREF,
23 | useValue: '<%= APP_BASE %>'
24 | }],
25 | bootstrap: [AppComponent]
26 | })
27 | export class AppModule { }
28 |
--------------------------------------------------------------------------------
/frontend/src/client/app/center/center.component.css:
--------------------------------------------------------------------------------
1 | .center-container{
2 | padding:20px;
3 | }
4 |
5 | .center-container .show-empty{
6 | width: 360px;
7 | margin: 0 auto;
8 | height: 260px;
9 | text-align: center;
10 | padding-top: 200px;
11 | background: url(./assets/img/notfound.png) no-repeat center center;
12 | }
--------------------------------------------------------------------------------
/frontend/src/client/app/center/center.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | 尚未创建任何问卷,马上
创建一个吧!
4 |
5 |
6 |
7 |
18 |
19 |
20 |
问卷详情
21 |
22 |
23 |
24 |
25 |
26 |
问卷管理
27 |
28 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/frontend/src/client/app/center/center.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
2 |
3 | import { QuestionnaireService } from '../core/services/questionnaire.service';
4 | import { QuestionnaireModel, QuestionnaireState } from '../shared/models/questionnaire.model';
5 |
6 | @Component({
7 | moduleId: module.id,
8 | selector: 'sd-center',
9 | templateUrl: 'center.component.html',
10 | styleUrls: ['center.component.css']
11 | })
12 | export class CenterComponent implements OnInit {
13 |
14 | private questionnaires:QuestionnaireModel[] = [];
15 | private selectedQuestionnaire:QuestionnaireModel;
16 | private selectedIndex:number;
17 | private isEmpty:boolean;
18 |
19 | constructor(private cd:ChangeDetectorRef, private questionnaireService:QuestionnaireService) { }
20 |
21 | ngOnInit() {
22 | this.questionnaireService.getQuestionnaires()
23 | .subscribe(
24 | questionnaires => {
25 | // 后端返回空对象或者空的问卷数组
26 | if(!questionnaires || questionnaires.length === 0){
27 | this.isEmpty = true;
28 | return;
29 | }
30 | this.isEmpty = false;
31 | this.questionnaires = questionnaires;
32 | this.selectedQuestionnaire = this.questionnaires[0];
33 | this.selectedIndex = 0;
34 | },
35 | error => console.error(error)
36 | );
37 | }
38 |
39 | onSelect(questionnaire:QuestionnaireModel, index:number) {
40 | this.selectedQuestionnaire = questionnaire;
41 | this.selectedIndex = index;
42 | }
43 |
44 | onDeleteQuestionnaire() {
45 | this.questionnaireService.deleteQuestionnaire(this.selectedQuestionnaire.id)
46 | .subscribe(
47 | res => {
48 | this.questionnaires.splice(this.selectedIndex, 1);
49 | //全部删除
50 | if(this.questionnaires.length === 0){
51 | this.isEmpty = true;
52 | } else {
53 | this.selectedQuestionnaire = this.questionnaires[0];
54 | this.selectedIndex = 0;
55 | }
56 | },
57 | error => console.log(error)
58 |
59 | );
60 | }
61 |
62 | onPublishQuestionnaire(){
63 | this.questionnaireService.publishQuestionnaire(this.selectedQuestionnaire.id)
64 | .subscribe(
65 | questionnaire => {
66 | this.selectedQuestionnaire.state = QuestionnaireState.Published;
67 | this.questionnaires[this.selectedIndex] = Object.assign({}, this.selectedQuestionnaire);
68 | this.cd.detectChanges();
69 | },
70 | error => console.log(error)
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/frontend/src/client/app/center/center.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { RouterModule } from '@angular/router';
4 |
5 |
6 | import { CenterComponent } from './center.component';
7 | import { CenterSharedModule } from './shared/center-shared.module';
8 | import { UserModule } from '../user/user.module';
9 |
10 | @NgModule({
11 | imports: [CommonModule, RouterModule, CenterSharedModule, UserModule],
12 | declarations: [CenterComponent],
13 | exports: [CenterComponent, CenterSharedModule]
14 | })
15 | export class CenterModule { }
16 |
--------------------------------------------------------------------------------
/frontend/src/client/app/center/center.routes.ts:
--------------------------------------------------------------------------------
1 | import { Route } from '@angular/router';
2 |
3 | import { CenterComponent } from './index';
4 | import { AuthGuard } from '../core/services/auth-guard.service';
5 |
6 | export const CenterRoutes: Route[] = [
7 | {
8 | path: 'center',
9 | component: CenterComponent,
10 | canActivate: [AuthGuard]
11 | }
12 | ];
13 |
--------------------------------------------------------------------------------
/frontend/src/client/app/center/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This barrel file provides the export for the lazy loaded EditComponent.
3 | */
4 | export * from './center.component';
5 | export * from './center.routes';
6 | export * from './shared/index';
7 |
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/center-shared.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | import { QuestionnaireControlsComponent } from './questionnaire-controls/index';
5 | import { QuestionnaireDetailComponent } from './questionnaire-detail/index';
6 | import { QuestionnaireItemComponent } from './questionnaire-item/index';
7 |
8 | @NgModule({
9 | imports: [CommonModule],
10 | declarations: [QuestionnaireItemComponent, QuestionnaireDetailComponent, QuestionnaireControlsComponent],
11 | exports: [QuestionnaireItemComponent, QuestionnaireDetailComponent, QuestionnaireControlsComponent]
12 | })
13 | export class CenterSharedModule { }
14 |
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/index.ts:
--------------------------------------------------------------------------------
1 | export * from './questionnaire-controls/index';
2 | export * from './questionnaire-detail/index';
3 | export * from './questionnaire-item/index';
4 |
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/questionnaire-controls/index.ts:
--------------------------------------------------------------------------------
1 | export * from './questionnaire-controls.component';
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/questionnaire-controls/questionnaire-controls.component.css:
--------------------------------------------------------------------------------
1 | .control-text{
2 | display: block;
3 | text-align: center;
4 | font-size: 16px;
5 | }
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/questionnaire-controls/questionnaire-controls.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
13 |
14 |
15 |
19 |
20 |
21 |
25 |
26 |
27 |
31 |
32 |
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/questionnaire-controls/questionnaire-controls.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, Output } from '@angular/core';
2 | import { Router } from '@angular/router';
3 |
4 | import { QuestionnaireModel } from '../../../shared/models/questionnaire.model';
5 |
6 | @Component({
7 | moduleId: module.id,
8 | selector: 'questionnaire-controls',
9 | templateUrl: 'questionnaire-controls.component.html',
10 | styleUrls: ['questionnaire-controls.component.css']
11 | })
12 | export class QuestionnaireControlsComponent {
13 |
14 | @Input() questionnaire:QuestionnaireModel;
15 | @Output() deleteQuestionnaire: EventEmitter = new EventEmitter();
16 | @Output() publishQuestionnaire: EventEmitter = new EventEmitter();
17 |
18 | constructor(private router: Router) {}
19 |
20 | onEdit(){
21 | this.router.navigateByUrl('admin/edit/' + this.questionnaire.id);
22 | }
23 |
24 | onPreview(){
25 | this.router.navigateByUrl('published/' + this.questionnaire.id);
26 | }
27 |
28 | onDelete(){
29 | this.deleteQuestionnaire.emit();
30 | }
31 |
32 | onPublish(){
33 | this.publishQuestionnaire.emit();
34 | }
35 | }
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/questionnaire-detail/index.ts:
--------------------------------------------------------------------------------
1 | export * from './questionnaire-detail.component';
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/questionnaire-detail/questionnaire-detail.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | 问卷标题: |
4 | {{questionnaire.title}} |
5 |
6 |
7 | 问卷简介: |
8 | {{questionnaire.starter}} |
9 |
10 |
11 | 问题总数: |
12 | {{questionnaire.questionList.length}} |
13 |
14 |
15 | 创建时间: |
16 | {{questionnaire.createDate}} |
17 |
18 |
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/questionnaire-detail/questionnaire-detail.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 |
3 | import { QuestionnaireModel } from '../../../shared/models/questionnaire.model';
4 |
5 | @Component({
6 | moduleId: module.id,
7 | selector: 'questionnaire-detail',
8 | templateUrl: 'questionnaire-detail.component.html'
9 | })
10 | export class QuestionnaireDetailComponent {
11 |
12 | @Input() questionnaire: QuestionnaireModel;
13 |
14 | }
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/questionnaire-item/index.ts:
--------------------------------------------------------------------------------
1 | export * from './questionnaire-item.component';
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/questionnaire-item/questionnaire-item.component.html:
--------------------------------------------------------------------------------
1 | {{questionnaire.title}}
2 | {{stateText}}
3 | {{questionnaire.createDate}}
--------------------------------------------------------------------------------
/frontend/src/client/app/center/shared/questionnaire-item/questionnaire-item.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, OnChanges, SimpleChanges, Input } from '@angular/core';
2 |
3 | import { QuestionnaireModel, QuestionnaireState } from '../../../shared/models/questionnaire.model';
4 |
5 | @Component({
6 | moduleId: module.id,
7 | selector: 'questionnaire-item',
8 | templateUrl: 'questionnaire-item.component.html'
9 | })
10 | export class QuestionnaireItemComponent implements OnInit, OnChanges {
11 |
12 | @Input() questionnaire:QuestionnaireModel;
13 |
14 | private stateText:String;
15 | private stateClass:String;
16 |
17 | ngOnChanges(changes: SimpleChanges){
18 | let questionnaireChange = changes['questionnaire'];
19 | if(questionnaireChange.previousValue.state &&
20 | questionnaireChange.currentValue.state !== questionnaireChange.previousValue.state){
21 | this.questionnaire = changes['questionnaire'].currentValue;
22 | this.setState();
23 | }
24 | }
25 |
26 | ngOnInit() {
27 | this.setState();
28 | }
29 |
30 | setState(){
31 | switch(this.questionnaire.state){
32 | case QuestionnaireState.Created:
33 | this.stateText = '已创建';
34 | this.stateClass = 'label-warning';
35 | break;
36 | case QuestionnaireState.Published:
37 | this.stateText = '回收中';
38 | this.stateClass = 'label-info';
39 | break;
40 | case QuestionnaireState.Finished:
41 | this.stateText = '已结束';
42 | this.stateClass = 'label-success';
43 | break;
44 | default:
45 | break;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/frontend/src/client/app/core/core.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, Optional, SkipSelf } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { RouterModule } from '@angular/router';
4 |
5 | import { AuthGuard } from './services/auth-guard.service';
6 | import { LoginService } from './services/login.service';
7 | import { NavbarComponent } from './navbar/index';
8 | import { QuestionnaireService } from './services/questionnaire.service';
9 | import { RegisterService } from './services/register.service';
10 | import { UserService } from './services/user.service';
11 |
12 | @NgModule({
13 | imports: [CommonModule, RouterModule],
14 | declarations: [NavbarComponent],
15 | providers: [QuestionnaireService, UserService, LoginService, RegisterService, AuthGuard],
16 | exports: [NavbarComponent]
17 | })
18 | export class CoreModule {
19 |
20 | constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
21 | if (parentModule) {
22 | throw new Error(
23 | 'CoreModule is already loaded. Import it in the AppModule only');
24 | }
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/frontend/src/client/app/core/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This barrel file provides the exports for the core resources (services, components).
3 | */
4 | export * from './navbar/index';
5 | export * from './services/index';
6 |
--------------------------------------------------------------------------------
/frontend/src/client/app/core/navbar/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This barrel file provides the export for the shared NavbarComponent.
3 | */
4 | export * from './navbar.component';
5 |
--------------------------------------------------------------------------------
/frontend/src/client/app/core/navbar/navbar.component.css:
--------------------------------------------------------------------------------
1 | :host {
2 | border-color: #e1e1e1;
3 | border-style: solid;
4 | border-width: 0 0 1px;
5 | display: block;
6 | height: 48px;
7 | padding: 0 16px;
8 | }
9 |
10 | nav a {
11 | color: #8f8f8f;
12 | font-size: 14px;
13 | font-weight: 500;
14 | line-height: 48px;
15 | margin-right: 20px;
16 | text-decoration: none;
17 | vertical-align: middle;
18 | }
19 |
20 | nav a.router-link-active {
21 | color: #106cc8;
22 | }
23 |
--------------------------------------------------------------------------------
/frontend/src/client/app/core/navbar/navbar.component.html:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/client/app/core/navbar/navbar.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { Router } from '@angular/router';
3 |
4 | import { LoginService } from '../services/login.service';
5 | import { UserService } from '../services/user.service';
6 |
7 | /**
8 | * This class represents the navigation bar component.
9 | */
10 | @Component({
11 | moduleId: module.id,
12 | selector: 'sd-navbar',
13 | templateUrl: 'navbar.component.html',
14 | styleUrls: ['navbar.component.css'],
15 | })
16 | export class NavbarComponent {
17 |
18 | constructor(private userService:UserService, private loginService:LoginService, private router:Router) { }
19 |
20 | logout() {
21 | //this.loginService.logout();
22 | this.userService.isLogin = false;
23 | this.userService.userInfo = null;
24 | this.router.navigate(['']);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/frontend/src/client/app/core/services/auth-guard.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
3 |
4 | import { UserService } from './user.service';
5 |
6 | @Injectable()
7 | export class AuthGuard implements CanActivate {
8 |
9 | constructor(private userService:UserService, private route:Router) { }
10 |
11 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
12 | if(this.userService.isLogin) {
13 | return true;
14 | }
15 | this.route.navigate(['/admin/login'],{queryParams:{returnUrl:state.url}});
16 | return false;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/frontend/src/client/app/core/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from './user.service';
2 | export * from './questionnaire.service';
3 | export * from './login.service';
4 | export * from './register.service';
5 | export * from './auth-guard.service';
6 |
--------------------------------------------------------------------------------
/frontend/src/client/app/core/services/login.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { FormControl, FormGroup, Validators } from '@angular/forms';
3 | import { Http } from '@angular/http';
4 | import { Headers } from '@angular/http';
5 | import { Observable } from 'rxjs/Observable';
6 | import { Observer } from 'rxjs/Observer';
7 |
8 | import { FieldBase } from '../../user/shared/field/field-base';
9 | import { FieldText } from '../../user/shared/field/field-text';
10 | import { FieldValidators } from '../../user/shared/field/field-validators';
11 | import { SITE_HOST_URL } from '../../shared/index';
12 |
13 | @Injectable()
14 | export class LoginService {
15 |
16 | private loginUrl = `${SITE_HOST_URL}login`;
17 |
18 | constructor(private http: Http) { }
19 |
20 | getFields() {
21 | let fields: FieldBase[] = [
22 | new FieldText({
23 | key: 'username',
24 | label: '用户名',
25 | value: '',
26 | required: true,
27 | pattern: 'username',
28 | order: 1
29 | }),
30 | new FieldText({
31 | key: 'password',
32 | label: '密码',
33 | type: 'password',
34 | value: '',
35 | required: true,
36 | pattern: 'password',
37 | order: 2
38 | }),
39 | ];
40 | return fields.sort((a, b) => a.order - b.order);
41 | }
42 |
43 | toFormGroup(fields: FieldBase[]) {
44 | let group: any = {};
45 |
46 | fields.forEach(field => {
47 | group[field.key] =
48 | field.pattern ?
49 | new FormControl(field.value || '', (FieldValidators)[field.pattern]) :
50 | field.required ?
51 | new FormControl(field.value || '', Validators.required) :
52 | new FormControl(field.value || '');
53 | });
54 | return new FormGroup(group);
55 | }
56 |
57 | login(data: Object) {
58 | let body = JSON.stringify(data);
59 | let headers = new Headers();
60 | headers.append('Content-Type', 'application/json');
61 | return new Observable((observer: Observer)=>{
62 | this.http.post(this.loginUrl, body, { headers }).subscribe(res=>{
63 | let body = res.json();
64 | if (body && body.success) {
65 | // this.userService.isLogin = true;
66 | // this.userService.userName = data['username'];
67 | observer.next(res);
68 | observer.complete();
69 | }
70 | });
71 | });
72 | }
73 |
74 | logout() {
75 | //this.userService.isLogin = false;
76 | //this.userService.userName = '';
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/frontend/src/client/app/core/services/questionnaire.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Http, Headers, RequestOptions, Response } from '@angular/http';
3 | import { Observable } from 'rxjs/Rx';
4 |
5 | import { QuestionnaireModel } from '../../shared/models/questionnaire.model';
6 | import { SITE_HOST_URL } from '../../shared/index';
7 |
8 | @Injectable()
9 | export class QuestionnaireService {
10 |
11 | constructor(private http:Http) { }
12 |
13 | private handleError(error: Response) {
14 | console.error(error);
15 | return Observable.throw(error.json().error || 'server error');
16 | }
17 |
18 | //根据id获取问卷信息
19 | getQuestionnaireById(id: string) {
20 | return this.http.get(SITE_HOST_URL + 'questionnaire/' + id)
21 | .map(res => res.json().data)
22 | .catch(this.handleError);
23 | }
24 |
25 | getQuestionnaires() {
26 | return this.http.get(SITE_HOST_URL + 'questionnaires')
27 | .map(res => res.json().data)
28 | .catch(this.handleError);
29 | }
30 |
31 | //添加新问卷
32 | addQuestionnaire(questionnaire:QuestionnaireModel) {
33 | let body = JSON.stringify(questionnaire);
34 | let headers = new Headers({'Content-Type':'application/json'});
35 | let options = new RequestOptions({headers:headers});
36 |
37 | return this.http.post(SITE_HOST_URL + 'questionnaire/add', body, options)
38 | .map(res => res.json().data)
39 | .catch(this.handleError);
40 | }
41 |
42 | //删除已有问卷
43 | deleteQuestionnaire(id: string) {
44 | return this.http.get(SITE_HOST_URL + 'questionnaire/delete/' + id)
45 | .map(res =>