├── .browserslistrc
├── .gitignore
├── README.md
├── babel.config.js
├── config.properties
├── database.sql
├── nodemon.json
├── package.json
├── pm2.json
├── postcss.config.js
├── server.babel.config.js
├── src
├── client
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ ├── http.js
│ ├── index.html
│ ├── main.js
│ ├── router.js
│ ├── store
│ │ └── index.js
│ └── views
│ │ ├── About.vue
│ │ ├── Home.vue
│ │ └── todolist
│ │ └── index.vue
├── dev.js
└── server
│ ├── api
│ └── TodoApi.js
│ ├── app.js
│ ├── container
│ └── index.js
│ ├── daos
│ ├── ItemDao.js
│ └── base.js
│ ├── initialize
│ ├── index.js
│ ├── properties.js
│ └── sequelize.js
│ ├── main.js
│ ├── middleware
│ ├── base.js
│ └── transactions.js
│ ├── models
│ ├── ItemModel.js
│ └── index.js
│ ├── services
│ ├── TodoService.js
│ └── base.js
│ └── util.js
├── vue.config.js
└── yarn.lock
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 | /log
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
24 | .vscode
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Welcome to Express-Vue App
2 |
3 | server与client一个命令启动。
4 |
5 | =========================
6 |
7 | 根据database.sql创建mysql数据库后和table后,修改config.properties配置
8 |
9 | 开发:
10 |
11 | ```
12 | npm run dev
13 | // or yarn dev
14 | ```
15 |
16 | 部署:
17 |
18 | ```
19 | npm run build && npm run start
20 | // or yarn build && yarn start
21 | ```
22 |
23 | ### 服务端技术栈
24 |
25 | * [框架 Express](http://expressjs.com/)
26 | * [热更新 nodemon](https://github.com/remy/nodemon)
27 | * [依赖注入 awilix](https://github.com/jeffijoe/awilix)
28 | * [数据持久化 sequelize](https://github.com/sequelize/sequelize)
29 | * [部署 pm2](https://pm2.io/)
30 |
31 | ### 客户端技术栈
32 |
33 | * [vue-router](https://router.vuejs.org)
34 | * [vuex](https://vuex.vuejs.org)
35 | * [axios](https://github.com/axios/axios)
36 | * [vue-class-component](https://github.com/vuejs/vue-class-component)
37 | * [vue-property-decorator](https://github.com/kaorun343/vue-property-decorator#readme)
38 | * [vuex-class](https://github.com/ktsn/vuex-class)
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "presets": [
3 | ["@vue/app", {
4 | "loose": true,
5 | "decoratorsLegacy": true
6 | }]
7 | ]
8 | }
--------------------------------------------------------------------------------
/config.properties:
--------------------------------------------------------------------------------
1 | [mysql]
2 | host=127.0.0.1
3 | port=3306
4 | user=root
5 | password=root
6 | database=test
--------------------------------------------------------------------------------
/database.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE `item` (
2 | `record_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
3 | `name` varchar(500) DEFAULT NULL,
4 | `state` int(11) DEFAULT NULL,
5 | PRIMARY KEY (`record_id`)
6 | ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;
--------------------------------------------------------------------------------
/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "ignore": [
3 | ".git",
4 | "node_modules/**/node_modules",
5 | "src/client"
6 | ]
7 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "express-vue-web-slush",
3 | "version": "1.0.0",
4 | "description": "express、vue脚手架",
5 | "main": "index.js",
6 | "scripts": {
7 | "clean": "rimraf dist",
8 | "dev-env": "cross-env NODE_ENV=development",
9 | "pro-env": "cross-env NODE_ENV=production",
10 | "start:client": "vue-cli-service serve",
11 | "build:client": "vue-cli-service build",
12 | "build:server": "babel --config-file ./server.babel.config.js src/server --out-dir dist/server/",
13 | "babel-server": "npm run dev-env && babel-node --config-file ./server.babel.config.js -- ./src/server/main.js",
14 | "build": "npm run clean && npm run build:client && npm run build:server",
15 | "dev": "babel-node --config-file ./server.babel.config.js -- ./src/dev.js",
16 | "start": "pm2 start pm2.json",
17 | "stop": "pm2 delete pm2.json"
18 | },
19 | "dependencies": {
20 | "axios": "^0.18.0",
21 | "core-js": "^2.6.5",
22 | "vue": "^2.6.10",
23 | "vue-property-decorator": "^8.1.1",
24 | "vue-router": "^3.0.3",
25 | "vuex": "^3.0.1",
26 | "vuex-class": "^0.3.2",
27 | "vuex-module-decorators": "^0.9.8"
28 | },
29 | "devDependencies": {
30 | "@babel/cli": "^7.4.4",
31 | "@babel/core": "^7.4.4",
32 | "@babel/node": "^7.2.2",
33 | "@babel/plugin-proposal-class-properties": "^7.4.4",
34 | "@babel/plugin-proposal-decorators": "^7.4.4",
35 | "@babel/plugin-transform-runtime": "^7.4.4",
36 | "@babel/preset-env": "^7.4.4",
37 | "@vue/cli-plugin-babel": "^3.7.0",
38 | "@vue/cli-service": "^3.7.0",
39 | "awilix": "^4.2.2",
40 | "awilix-express": "^2.1.1",
41 | "body-parser": "^1.19.0",
42 | "cookie-parser": "^1.4.4",
43 | "cross-env": "^5.2.0",
44 | "express": "^4.16.4",
45 | "mysql2": "^1.6.5",
46 | "node-sass": "^4.9.0",
47 | "nodemon": "^1.19.0",
48 | "pm2": "^3.5.1",
49 | "properties": "^1.2.1",
50 | "rimraf": "^2.6.3",
51 | "sass-loader": "^7.1.0",
52 | "sequelize": "^5.15.1",
53 | "vue-template-compiler": "^2.5.21"
54 | },
55 | "repository": {
56 | "type": "git",
57 | "url": "git+https://github.com/yacan8/express-vue-web-slush.git"
58 | },
59 | "keywords": [
60 | "vue",
61 | "express"
62 | ],
63 | "author": "can.yang",
64 | "license": "ISC",
65 | "bugs": {
66 | "url": "https://github.com/yacan8/express-vue-web-slush/issues"
67 | },
68 | "homepage": "https://github.com/yacan8/express-vue-web-slush#readme"
69 | }
70 |
--------------------------------------------------------------------------------
/pm2.json:
--------------------------------------------------------------------------------
1 | {
2 | "apps": [
3 | {
4 | "name": "vue-express",
5 | "script": "./dist/server/main.js",
6 | "log_date_format": "YYYY-MM-DD HH:mm Z",
7 | "output": "./log/out.log",
8 | "error": "./log/error.log",
9 | "instances": 1,
10 | "watch": false,
11 | "merge_logs": true,
12 | "env": {
13 | "NODE_ENV": "production"
14 | }
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/server.babel.config.js:
--------------------------------------------------------------------------------
1 | var isDebug = process.env.NODE_ENV === 'development';
2 |
3 | var config = {
4 | presets: [
5 | ['@babel/env', {
6 | useBuiltIns: false,
7 | targets: {
8 | node: 'current'
9 | }
10 | }]
11 | ],
12 | plugins: [
13 | ['@babel/plugin-proposal-decorators', {legacy: true}],
14 | ['@babel/plugin-proposal-class-properties', {loose: true}],
15 | '@babel/plugin-transform-runtime'
16 | ]
17 | };
18 |
19 | // 服务端开发环境babel配置文件 bable-node会自动添加polyfill,所以不需要添加preset/env
20 | // if (isDebug) {
21 | // config.presets = [
22 | // ['@babel/env', {
23 | // useBuiltIns: false,
24 | // targets: {
25 | // node: 'current'
26 | // }
27 | // }]
28 | // ]
29 | // }
30 |
31 | module.exports = config;
--------------------------------------------------------------------------------
/src/client/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TODO-LIST
5 |
6 |
7 |
8 |
9 |
10 |
31 |
--------------------------------------------------------------------------------
/src/client/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yacan8/express-vue-web-slush/0f315e02c73c01991abff9bf7748c83ba4de4108/src/client/assets/logo.png
--------------------------------------------------------------------------------
/src/client/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
服务端技术栈
5 |
11 |
客户端技术栈
12 |
20 |
21 |
22 |
23 |
34 |
35 |
36 |
52 |
--------------------------------------------------------------------------------
/src/client/http.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file http接口统一管理
3 | */
4 | import axios from 'axios';
5 | // 初始化axios
6 | axios.defaults.timeout = 10000;
7 | // axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
8 | axios.defaults.headers.post['Content-Type'] = 'application/json;charset=utf-8';
9 | axios.defaults.withCredential = true;
10 | axios.defaults.crossDomain = true;
11 |
12 | /**
13 | * get方法,对应get请求
14 | * @param {string} url [请求的url地址]
15 | * @param {Object} params [请求时携带的参数]
16 | * @param {Object} config [配置]
17 | * @return {Object} Promise
18 | */
19 | export function get(url, params = {}, config = {}) {
20 | return new Promise((resolve, reject) => {
21 | axios
22 | .get(url, {
23 | params: params
24 | }, config)
25 | .then(res => {
26 | resolve(res.data);
27 | })
28 | .catch(err => {
29 | reject(err);
30 | });
31 | });
32 | }
33 |
34 | /**
35 | * post方法,对应post请求
36 | * @param {string} url [请求的url地址]
37 | * @param {Object} params [请求时携带的参数]
38 | * @param {Object} config [配置]
39 | * @return {Object} Promise
40 | */
41 | export function post(url, params, config = {}) {
42 | return new Promise((resolve, reject) => {
43 | axios
44 | .post(url, params, config)
45 | .then(res => {
46 | resolve(res.data);
47 | })
48 | .catch(err => {
49 | reject(err);
50 | });
51 | });
52 | }
--------------------------------------------------------------------------------
/src/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | client
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/client/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import router from './router'
4 | import store from './store/'
5 |
6 | Vue.config.productionTip = false
7 |
8 | new Vue({
9 | router,
10 | store,
11 | render: h => h(App)
12 | }).$mount('#app')
13 |
--------------------------------------------------------------------------------
/src/client/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import Home from './views/Home.vue'
4 |
5 | Vue.use(Router)
6 |
7 | export default new Router({
8 | routes: [
9 | {
10 | path: '/',
11 | name: 'home',
12 | component: Home
13 | }
14 | ]
15 | })
16 |
--------------------------------------------------------------------------------
/src/client/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import { get, post } from '../http';
4 |
5 | Vue.use(Vuex)
6 |
7 | const handleError = e => {
8 | console.error(e);
9 | return {
10 | success: false,
11 | message: '客户端异常'
12 | }
13 | }
14 |
15 | export default new Vuex.Store({
16 | state: {
17 | todolist: []
18 | },
19 | mutations: {
20 | setTodolist(state, todolist) {
21 | state.todolist = todolist;
22 | }
23 | },
24 | actions: {
25 | fetchTodolist({commit}, querys = {}) {
26 | return get('/api/todo/getTodolist', querys).then(res => {
27 | if (res.success) {
28 | commit('setTodolist', res.data);
29 | }
30 | return res;
31 | }).catch(e => handleError(e));
32 | },
33 | addTodoItem({commit, rootState}, item) {
34 | return post('/api/todo/addTodoItem', item).then(res => {
35 | if (res.success) {
36 | const { todolist } = rootState;
37 | todolist.push(item);
38 | commit('setTodolist', todolist);
39 | }
40 | return res;
41 | }).catch(e => handleError(e));
42 | },
43 | doToggleState({commit, rootState}, item) {
44 | const state = item.state == 1 ? 0 : 1;
45 | item.state = state;
46 | return post('/api/todo/updateItem', item).then(res => {
47 | if (res.success) {
48 | const { todolist } = rootState;
49 | const itemItem = todolist.findIndex(i => i.name === item.name);
50 | todolist[itemItem].state = state;
51 | commit('setTodolist', todolist);
52 | }
53 | return res;
54 | }).catch(e => handleError(e));
55 | }
56 | }
57 | })
58 |
--------------------------------------------------------------------------------
/src/client/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is an about page
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/client/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 |
7 |
8 |
9 |
35 |
--------------------------------------------------------------------------------
/src/client/views/todolist/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
加载中...
8 |
13 |
16 |
17 |
18 |
19 |
118 |
119 |
145 |
--------------------------------------------------------------------------------
/src/dev.js:
--------------------------------------------------------------------------------
1 | import * as childProcess from 'child_process';
2 |
3 | function run() {
4 | const client = childProcess.spawn('vue-cli-service', ['serve']);
5 | client.stdout.on('data', x => process.stdout.write(x));
6 | client.stderr.on('data', x => process.stderr.write(x));
7 |
8 | const server = childProcess.spawn('nodemon', ['--exec', 'npm run babel-server'], {
9 | env: Object.assign({
10 | NODE_ENV: 'development'
11 | }, process.env),
12 | silent: false
13 | });
14 | server.stdout.on('data', x => process.stdout.write(x));
15 | server.stderr.on('data', x => process.stderr.write(x));
16 |
17 | process.on('exit', () => {
18 | server.kill('SIGTERM');
19 | client.kill('SIGTERM');
20 | });
21 | }
22 |
23 | run();
--------------------------------------------------------------------------------
/src/server/api/TodoApi.js:
--------------------------------------------------------------------------------
1 | import { route, GET, POST } from 'awilix-express';
2 |
3 | @route('/todo')
4 | export default class TodoAPI {
5 |
6 | constructor({ todoService }) {
7 | this.todoService = todoService;
8 | }
9 |
10 | @route('/getData')
11 | @GET()
12 | async getData(req, res) {
13 | res.successPrint('请求成功', {
14 | info: 'Welcome to Express-Vue App'
15 | })
16 | }
17 |
18 | @route('/getTodolist')
19 | @GET()
20 | async getTodolist(req, res) {
21 | const { success, data: todolist, message } = await this.todoService.getList();
22 | if (!success) {
23 | res.failPrint(message);
24 | return;
25 | }
26 | res.successPrint(message, todolist);
27 | }
28 |
29 | @route('/addTodoItem')
30 | @POST()
31 | async addTodoItem(req, res) {
32 | const { name, state } = req.body;
33 | const { success, message } = await this.todoService.addTodoItem({name, state});
34 | if (!success) {
35 | res.failPrint(message);
36 | return;
37 | }
38 | res.successPrint(message);
39 | }
40 |
41 |
42 | @route('/updateItem')
43 | @POST()
44 | async updateItem(req, res) {
45 | const { name, state } = req.body;
46 | const { success, message } = await this.todoService.updateItem({name, state});
47 | if (!success) {
48 | res.failPrint(message);
49 | return;
50 | }
51 | res.successPrint(message);
52 | }
53 | }
--------------------------------------------------------------------------------
/src/server/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file app
3 | */
4 | import express from 'express';
5 | import { Lifetime, asClass } from 'awilix';
6 | import { loadControllers, scopePerRequest } from 'awilix-express';
7 | import container from './container';
8 | import bodyParser from 'body-parser';
9 | import path from 'path';
10 | import fs from 'fs';
11 | import { baseMiddleware } from './middleware/base';
12 | import initialize from './initialize';
13 | import cookieParser from 'cookie-parser';
14 |
15 | const app = express();
16 | app.use(express.static(path.resolve(__dirname, '../client')));
17 | app.use(cookieParser());
18 | app.use(bodyParser());
19 | app.use(scopePerRequest(container));
20 | app.use(baseMiddleware(app));
21 | app.use('/api', loadControllers('api/*.js', {
22 | cwd: __dirname,
23 | lifetime: Lifetime.SINGLETON
24 | }));
25 |
26 | export default async function run() {
27 | await initialize(app);
28 |
29 | // 依赖注入配置service层和dao层
30 | container.loadModules(['services/*Service.js', 'daos/*Dao.js'], {
31 | formatName: 'camelCase',
32 | register: asClass,
33 | cwd: path.resolve(__dirname)
34 | });
35 |
36 | app.get('*', (req, res) => {
37 | const html = fs.readFileSync(path.resolve(__dirname, '../client', 'index.html'), 'utf-8');
38 | res.send(html);
39 | });
40 |
41 | app.listen(9001, err => {
42 | if (err) {
43 | console.error(err);
44 | return;
45 | }
46 | console.log('Listening at http://localhost:9001');
47 | });
48 | }
--------------------------------------------------------------------------------
/src/server/container/index.js:
--------------------------------------------------------------------------------
1 | import * as awilix from 'awilix';
2 |
3 | const container = awilix.createContainer({
4 | injectionMode: awilix.InjectionMode.PROXY
5 | });
6 |
7 | export default container;
--------------------------------------------------------------------------------
/src/server/daos/ItemDao.js:
--------------------------------------------------------------------------------
1 | import BaseDao from './base';
2 |
3 | export default class ItemDao extends BaseDao {
4 |
5 | modelName = 'Item';
6 |
7 | constructor(modules) {
8 | super(modules);
9 | }
10 |
11 | async getList() {
12 | return await this.findAll();
13 | }
14 |
15 | async addItem(item) {
16 | return await this.insert(item);
17 | }
18 | }
--------------------------------------------------------------------------------
/src/server/daos/base.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file base dao
3 | */
4 | import db from '../models';
5 | import { promiseCatch } from '../util';
6 |
7 | export default class BaseDao {
8 |
9 | constructor(modules) {
10 | const request = modules.request;
11 | this.transaction = request.transaction;
12 | this.sequelize = db.sequelize;
13 | }
14 |
15 | insert(params) {
16 | const Model = this.getModel();
17 | const config = this.getConfig();
18 | return promiseCatch(Model.create(params, config));
19 | }
20 |
21 | batchInsert(params) {
22 | const Model = this.getModel();
23 | const config = this.getConfig();
24 | return promiseCatch(Model.bulkCreate(params, config));
25 | }
26 |
27 | delete(params) {
28 | const Model = this.getModel();
29 | const config = this.getConfig();
30 | return promiseCatch(Model.destroy({
31 | ...config,
32 | ...params
33 | }));
34 | }
35 |
36 | findOne(params) {
37 | const Model = this.getModel();
38 | return promiseCatch(Model.findOne(params).then(models => {
39 | return models || null;
40 | }));
41 | }
42 |
43 | findAll(params) {
44 | const Model = this.getModel();
45 | return promiseCatch(Model.findAll(params));
46 | }
47 |
48 | update(params, query) {
49 | const Model = this.getModel();
50 | const config = this.getConfig();
51 | return promiseCatch(Model.update(params, {
52 | ...config,
53 | ...query
54 | }));
55 | }
56 |
57 | getModel() {
58 | return db[this.modelName];
59 | }
60 |
61 | getConfig() {
62 | const config = {};
63 | if (this.transaction) {
64 | config.transaction = this.transaction;
65 | }
66 | return config;
67 | }
68 |
69 | commit() {
70 | if (this.transaction) {
71 | return promiseCatch(this.transaction.commit());
72 | }
73 | return promiseCatch(Promise.resolve());
74 | }
75 |
76 | rollback() {
77 | if (this.transaction) {
78 | return promiseCatch(this.transaction.rollback());
79 | }
80 | return promiseCatch(Promise.resolve());
81 | }
82 | }
--------------------------------------------------------------------------------
/src/server/initialize/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file init
3 | */
4 | import loadProperties from './properties';
5 | import { initSequelize } from './sequelize';
6 | import container from '../container';
7 | import * as awilix from 'awilix';
8 | import { installModel } from '../models';
9 |
10 | export default async function initialize() {
11 | const config = await loadProperties();
12 | const { mysql } = config;
13 | const sequelize = initSequelize(mysql);
14 | installModel(sequelize);
15 | container.register({
16 | globalConfig: awilix.asValue(config),
17 | sequelize: awilix.asValue(sequelize)
18 | });
19 | }
--------------------------------------------------------------------------------
/src/server/initialize/properties.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file properties
3 | */
4 | import properties from 'properties';
5 | import path from 'path';
6 |
7 | const propertiesPath = path.resolve(process.cwd(), 'config.properties');
8 |
9 | export default function load() {
10 | return new Promise((resolve, reject) => {
11 | properties.parse(propertiesPath, { path: true, sections: true }, (err, obj) => {
12 | if (err) {
13 | reject(err);
14 | return;
15 | }
16 | resolve(obj);
17 | });
18 | }).catch(e => {
19 | console.error(e);
20 | return {};
21 | });
22 | }
--------------------------------------------------------------------------------
/src/server/initialize/sequelize.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file sequelize
3 | */
4 | import Sequelize from 'sequelize';
5 |
6 | let sequelize;
7 |
8 | const defaultPreset = {
9 | host: 'localhost',
10 | dialect: 'mysql',
11 | operatorsAliases: false,
12 | port: 3306,
13 | pool: {
14 | max: 10,
15 | min: 0,
16 | acquire: 30000,
17 | idle: 10000
18 | }
19 | };
20 |
21 | export function initSequelize(config) {
22 | const { host, database, password, port, user } = config;
23 | sequelize = new Sequelize(database, user, password, Object.assign({}, defaultPreset, {
24 | host,
25 | port
26 | }));
27 | return sequelize;
28 | };
29 |
30 | export default sequelize;
--------------------------------------------------------------------------------
/src/server/main.js:
--------------------------------------------------------------------------------
1 | import run from './app';
2 |
3 | run();
--------------------------------------------------------------------------------
/src/server/middleware/base.js:
--------------------------------------------------------------------------------
1 | import { asValue } from 'awilix';
2 |
3 | export function baseMiddleware(app) {
4 | return (req, res, next) => {
5 | res.successPrint = (message, data) => {
6 | res.json({
7 | success: true,
8 | message,
9 | data
10 | });
11 | };
12 |
13 | res.failPrint = (message, data) => {
14 | res.json({
15 | success: false,
16 | message,
17 | data
18 | });
19 | };
20 |
21 | req.app = app;
22 |
23 | // 注入request、response
24 | req.container = req.container.createScope();
25 | req.container.register({
26 | request: asValue(req),
27 | response: asValue(res)
28 | });
29 | next();
30 | }
31 | }
--------------------------------------------------------------------------------
/src/server/middleware/transactions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file transactions
3 | */
4 | import { asValue } from 'awilix';
5 |
6 | export default function () {
7 | return function (req, res, next) {
8 | const sequelize = container.resolve('sequelize');
9 | sequelize.transaction({
10 | autocommit: false
11 | }).then(t => {
12 | req.container = req.container.createScope();
13 | req.container.register({
14 | transaction: asValue(t)
15 | });
16 | next();
17 | });
18 | }
19 | }
--------------------------------------------------------------------------------
/src/server/models/ItemModel.js:
--------------------------------------------------------------------------------
1 | export default function (sequelize, DataTypes) {
2 | const Item = sequelize.define('Item', {
3 | recordId: {
4 | type: DataTypes.INTEGER,
5 | field: 'record_id',
6 | primaryKey: true
7 | },
8 | name: {
9 | type: DataTypes.STRING,
10 | field: 'name'
11 | },
12 | state: {
13 | type: DataTypes.INTEGER,
14 | field: 'state'
15 | }
16 | }, {
17 | tableName: 'item',
18 | timestamps: false
19 | });
20 |
21 | return Item;
22 | }
--------------------------------------------------------------------------------
/src/server/models/index.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import Sequelize from 'sequelize';
4 |
5 | const db = {};
6 |
7 | export function installModel(sequelize) {
8 | fs.readdirSync(__dirname)
9 | .filter(file => (file.indexOf('.') !== 0 && file.slice(-3) === '.js' && file !== 'index.js'))
10 | .forEach((file) => {
11 | const model = sequelize.import(path.join(__dirname, file));
12 | db[model.name] = model;
13 | });
14 | Object.keys(db).forEach((modelName) => {
15 | if (db[modelName].associate) {
16 | db[modelName].associate(db);
17 | }
18 | });
19 | db.sequelize = sequelize;
20 | db.Sequelize = Sequelize;
21 | }
22 |
23 | export default db;
--------------------------------------------------------------------------------
/src/server/services/TodoService.js:
--------------------------------------------------------------------------------
1 | import BaseService from './base';
2 |
3 | export default class TodoService extends BaseService {
4 | constructor({ itemDao }) {
5 | super();
6 | this.itemDao = itemDao;
7 | }
8 |
9 | async getList() {
10 | const [err, list] = await this.itemDao.getList();
11 | if (err) {
12 | return this.fail('获取列表失败', null, err);
13 | }
14 | return this.success('查询成功', list);
15 | }
16 |
17 | async addTodoItem(item) {
18 | const [exsitErr, itemExsit] = await this.itemDao.findOne({
19 | where: {
20 | name: item.name
21 | }
22 | });
23 | if (exsitErr) {
24 | return this.fail('添加失败', null, err);
25 | }
26 | if (itemExsit) {
27 | return this.fail('已存在');
28 | }
29 | const [addErr] = await this.itemDao.addItem(item);
30 | if (addErr) {
31 | return this.fail('添加失败', null, err);
32 | }
33 | return this.success('添加成功')
34 | }
35 |
36 | async updateItem(item) {
37 | const [err, updateItem] = await this.itemDao.update(item, {
38 | where: {
39 | name: item.name,
40 | }
41 | });
42 | if (err) {
43 | return this.fail('更新失败');
44 | }
45 | return this.success('更新成功');
46 | }
47 | }
--------------------------------------------------------------------------------
/src/server/services/base.js:
--------------------------------------------------------------------------------
1 | export default class BaseService {
2 | success(message, data = null) {
3 | return {
4 | success: true,
5 | message,
6 | data
7 | }
8 | }
9 |
10 | fail(message, data = null, err) {
11 | if (err) {
12 | console.error(err);
13 | }
14 | return {
15 | success: false,
16 | message,
17 | data
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/server/util.js:
--------------------------------------------------------------------------------
1 | // 处理promise错误
2 | export function promiseCatch(promise) {
3 | return promise.then(data => [null, data]).catch(err => [err]);
4 | }
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file vue-cli config
3 | */
4 | const path = require('path');
5 | const clientPath = path.resolve(process.cwd(), './src/client');
6 | module.exports = {
7 | configureWebpack: {
8 | entry: [
9 | path.resolve(clientPath, 'main.js')
10 | ],
11 | resolve: {
12 | alias: {
13 | '@': clientPath
14 | }
15 | },
16 | devServer: {
17 | proxy: {
18 | '/api': {
19 | target: 'http://localhost:9001'
20 | }
21 | }
22 | }
23 | },
24 | outputDir: './dist/client/'
25 | };
--------------------------------------------------------------------------------