├── src
├── libs
│ └── .gitkeep
├── less
│ ├── icons.less
│ ├── app.less
│ ├── variables.less
│ ├── utils.less
│ ├── base.less
│ ├── mixins.less
│ └── reset.less
├── plugins
│ └── .gitkeep
├── assets
│ ├── fonts
│ │ └── .gitkeep
│ └── images
│ │ └── .gitkeep
├── store
│ ├── modules
│ │ └── .gitkeep
│ ├── config.js
│ ├── types.js
│ ├── mutations.js
│ ├── plugins
│ │ └── cache.js
│ ├── index.js
│ └── actions.js
├── view
│ ├── modules
│ │ └── .gitkeep
│ ├── Hello.vue
│ └── Tree.vue
├── router
│ ├── map
│ │ ├── hello.js
│ │ └── tree.js
│ └── index.js
├── components
│ └── tree
│ │ ├── index.js
│ │ ├── tree.vue
│ │ └── treeItem.vue
├── main.js
├── api
│ ├── city.js
│ └── ajax.js
├── App.vue
├── index.html
└── util
│ └── cache.js
├── upload
└── .gitkeep
├── .eslintignore
├── dic.yml
├── .editorconfig
├── .gitignore
├── LICENSE
├── .eslintrc.js
├── mock
└── city.js
├── package.json
└── README.md
/src/libs/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/upload/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/less/icons.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/plugins/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/fonts/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/images/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/store/modules/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/view/modules/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | public/*.js
2 | build/*.js
3 |
--------------------------------------------------------------------------------
/src/store/config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'city': 'CACHE_CITY'
3 | }
4 |
--------------------------------------------------------------------------------
/dic.yml:
--------------------------------------------------------------------------------
1 | action:
2 | add: 添加
3 | get: 获取
4 |
5 | model:
6 | project: 项目
7 | user: 用户
8 |
--------------------------------------------------------------------------------
/src/router/map/hello.js:
--------------------------------------------------------------------------------
1 | import Hello from 'view/Hello'
2 | export default [
3 | { path: '/', component: Hello }
4 | ]
5 |
--------------------------------------------------------------------------------
/src/router/map/tree.js:
--------------------------------------------------------------------------------
1 | import Tree from 'view/Tree'
2 |
3 | export default [
4 | { path: '/tree', component: Tree }
5 | ]
6 |
--------------------------------------------------------------------------------
/src/components/tree/index.js:
--------------------------------------------------------------------------------
1 | import tree from './tree.vue'
2 | import treeItem from './treeItem.vue'
3 |
4 | export default tree
5 | export { treeItem }
6 |
--------------------------------------------------------------------------------
/src/less/app.less:
--------------------------------------------------------------------------------
1 | @import "variables.less";
2 | @import "mixins.less";
3 | @import "reset.less";
4 | @import "base.less";
5 | @import "icons.less";
6 | @import "utils.less";
7 |
--------------------------------------------------------------------------------
/src/store/types.js:
--------------------------------------------------------------------------------
1 | export const city = {
2 | SYNC_DATA: 'city/SYNC_DATA',
3 | REMOVE_ONE: 'city/REMOVE_ONE',
4 | }
5 | export const COMMIT_ACTION = 'COMMIT_ACTION'
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 4
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 |
10 | [*.{less,json,yml}]
11 | indent_size = 2
12 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App'
3 | import router from './router'
4 | import store from './store'
5 |
6 | export default new Vue({
7 | el: '#app',
8 | router,
9 | store,
10 | render: h => h(App)
11 | })
12 |
--------------------------------------------------------------------------------
/src/api/city.js:
--------------------------------------------------------------------------------
1 | import Ajax, { GET } from './ajax'
2 |
3 | // 通过 Ajax 类实例化拥有基础的 QRUD 能力
4 | const CityApi = new Ajax('/city')
5 |
6 | // 如果需要额外的个性化请求,借助 GET、POST、PUT、DELETE 工具方法添加
7 | CityApi.paging = () => GET('/city')
8 |
9 | export default CityApi
10 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from 'vue-router'
3 | import hello from './map/hello'
4 | import tree from './map/tree'
5 |
6 | Vue.use(VueRouter)
7 |
8 | export default new VueRouter({
9 | routes: [
10 | ...hello,
11 | ...tree
12 | ]
13 | })
14 |
--------------------------------------------------------------------------------
/src/store/mutations.js:
--------------------------------------------------------------------------------
1 | // import _ from 'lodash'
2 | import { city as cityType } from './types'
3 |
4 | export default {
5 | [cityType.SYNC_DATA](state, data) {
6 | state.citys = data
7 | },
8 | // [cityType.REMOVE_ONE](state, id) {
9 | // state.citys = _.omit(state.citys, [id])
10 | // }
11 | }
12 |
--------------------------------------------------------------------------------
/src/less/variables.less:
--------------------------------------------------------------------------------
1 | @font-family-base: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
2 | @font-size-base: 14px;
3 | @line-height-base: 1.5;
4 | @text-color: #333;
5 | @body-bg: #fff;
6 |
7 | // link
8 | @link-color: #20a0ff;
9 | @link-hover-color: darken(#20a0ff, 10%);
10 | @link-hover-decoration: underline;
11 |
--------------------------------------------------------------------------------
/src/store/plugins/cache.js:
--------------------------------------------------------------------------------
1 | import Cache from 'src/util/cache'
2 | import Config from '../config'
3 |
4 | export default store => {
5 | store.subscribe(({ type, payload }, state) => {
6 | const module = type.split('/')[0]
7 | const cacheKey = Config[module]
8 | if (cacheKey) {
9 | Cache.save(cacheKey, payload)
10 | }
11 | })
12 | }
13 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import mutations from './mutations'
4 | import actions from './actions'
5 | import Cache from './plugins/cache'
6 |
7 | Vue.use(Vuex)
8 |
9 | export default new Vuex.Store({
10 | state: {
11 | citys: {}
12 | },
13 | mutations,
14 | actions,
15 | getters: {
16 | citysCount(state) {
17 | return Object.keys(state.citys).length
18 | }
19 | },
20 | plugins: [
21 | Cache
22 | ]
23 | })
24 |
--------------------------------------------------------------------------------
/src/components/tree/tree.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
26 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 | <% if (htmlWebpackPlugin.options.NODE_ENV === 'dev') { %>
10 |
11 | <% } %>
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/less/utils.less:
--------------------------------------------------------------------------------
1 | .clearfix {
2 | .clearfix()
3 | }
4 | .center-block {
5 | .center-block()
6 | }
7 | .text-overflow {
8 | .text-overflow()
9 | }
10 | .pull-right {
11 | float: right !important
12 | }
13 | .pull-left {
14 | float: left !important
15 | }
16 | .abs-left,
17 | .abs-right {
18 | position: absolute;
19 | top: 0;
20 | }
21 | .abs-left {
22 | left: 0
23 | }
24 | .abs-right {
25 | right: 0
26 | }
27 |
28 | .hide {
29 | display: none !important
30 | }
31 | .show {
32 | display: block !important
33 | }
34 | .invisible {
35 | visibility: hidden
36 | }
37 |
38 | .hidden {
39 | display: none !important;
40 | visibility: hidden !important;
41 | }
42 |
43 | .affix {
44 | position: fixed
45 | }
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # build dir
7 | public
8 |
9 | # e2e test reports
10 | test/e2e/reports
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 |
23 | # nyc test coverage
24 | .nyc_output
25 |
26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
27 | .grunt
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules
37 | jspm_packages
38 |
39 | # Optional npm cache directory
40 | .npm
41 |
42 | # Optional REPL history
43 | .node_repl_history
44 |
--------------------------------------------------------------------------------
/src/store/actions.js:
--------------------------------------------------------------------------------
1 | import CityApi from 'api/city'
2 | import Config from './config'
3 | import Cache from 'src/util/cache'
4 | import { city as cityType, COMMIT_ACTION } from './types'
5 | import _ from 'lodash'
6 |
7 | export default {
8 | getCitys({ dispatch }) {
9 | dispatch(COMMIT_ACTION, {
10 | type: cityType.SYNC_DATA,
11 | promise: () => CityApi.all().then(data => data.citys)
12 | })
13 | },
14 | deleteCity({ commit, state }, id) {
15 | commit(cityType.SYNC_DATA, _.omit(state.citys, [id]))
16 | },
17 | [COMMIT_ACTION]({ commit }, { type, promise }) {
18 | const module = type.split('/')[0]
19 | const cacheKey = Config[module] || ''
20 | const cacheData = Cache.get(cacheKey)
21 | if(!cacheData) {
22 | promise().then(data => commit(type, data))
23 | } else {
24 | commit(type, cacheData)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/less/base.less:
--------------------------------------------------------------------------------
1 | *,
2 | *:before,
3 | *:after {
4 | box-sizing: border-box
5 | }
6 |
7 | html {
8 | font-size: 10px;
9 | -webkit-tap-highlight-color: rgba(0,0,0,0);
10 | }
11 |
12 | body {
13 | font-family: @font-family-base;
14 | font-size: @font-size-base;
15 | line-height: @line-height-base;
16 | color: @text-color;
17 | background-color: @body-bg;
18 | }
19 |
20 | input,
21 | button,
22 | select,
23 | textarea {
24 | font-family: inherit;
25 | font-size: inherit;
26 | line-height: inherit;
27 | }
28 |
29 | a {
30 | color: @link-color;
31 | text-decoration: none;
32 |
33 | &:hover,
34 | &:focus {
35 | color: @link-hover-color;
36 | text-decoration: @link-hover-decoration;
37 | }
38 |
39 | &:focus {
40 | .tab-focus();
41 | }
42 | }
43 |
44 | ul.default,
45 | ol.default {
46 | padding-left: 30px;
47 | }
48 | ul.default {
49 | list-style: disc
50 | }
51 | ol.default {
52 | list-style: decimal
53 | }
54 |
--------------------------------------------------------------------------------
/src/view/Hello.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Demo1
4 |
5 |
8 |
共 {{citysCount}} 条数据
9 |
10 |
Demo2
11 |
tree
12 |
13 |
14 |
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016
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 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: 'babel-eslint',
4 | parserOptions: {
5 | ecmaVersion: 6,
6 | sourceType: 'module'
7 | },
8 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
9 | extends: 'standard',
10 | // required to lint *.vue files
11 | plugins: [
12 | 'html'
13 | ],
14 | env: {
15 | 'browser': true,
16 | 'node': true
17 | },
18 | globals: {
19 | '_': true
20 | },
21 | // add your custom rules here
22 | rules: {
23 | // allow paren-less arrow functions
24 | 'arrow-parens': 0,
25 | // allow async-await
26 | 'generator-star-spacing': 0,
27 | //
28 | 'space-before-function-paren': [0, "always"],
29 | 'comma-dangle': ['error', 'ignore'],
30 | "keyword-spacing": 0,
31 | // 4 space indent
32 | 'indent': [2, 4, { "SwitchCase": 1 }],
33 | // disable no-new
34 | 'no-new': 0,
35 | // allow debugger during development
36 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/view/Tree.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
47 |
--------------------------------------------------------------------------------
/src/util/cache.js:
--------------------------------------------------------------------------------
1 | /**
2 | * localStorage
3 | */
4 | const __localStorage = window.localStorage
5 |
6 | /**
7 | * 检测是否支持 localStorage
8 | */
9 | const isSupported = () => {
10 | const key = '__EPM_TEST_SUPPORTED__'
11 | try {
12 | __localStorage.setItem(key, Date.now())
13 | __localStorage.removeItem(key)
14 | return true
15 | } catch (error) {
16 | return false
17 | }
18 | }
19 |
20 | /**
21 | * browserStorage
22 | */
23 | const browserStorage = () => {
24 | const storage = __localStorage
25 | return {
26 | get: key => JSON.parse(storage.getItem(key)),
27 | save: (key, data) => storage.setItem(key, JSON.stringify(data)),
28 | remove: key => storage.removeItem(key),
29 | clear: () => storage.clear()
30 | }
31 | }
32 |
33 | /**
34 | * ramStorage
35 | */
36 | const ramStorage = () => {
37 | const storage = new Map()
38 | return {
39 | get: key => storage.get(key),
40 | save: (key, data) => storage.set(key, data),
41 | remove: key => storage.delete(key),
42 | clear: () => storage.clear()
43 | }
44 | }
45 |
46 | /**
47 | * cacheStore fallback
48 | * 不支持 localStorage 时,回退 ramStorage
49 | */
50 | const cacheStore = isSupported() ? browserStorage() : ramStorage()
51 | export default cacheStore
52 |
--------------------------------------------------------------------------------
/src/less/mixins.less:
--------------------------------------------------------------------------------
1 | .size(@width; @height) {
2 | width: @width;
3 | height: @height;
4 | }
5 |
6 | .circle(@size) {
7 | .size(@size, @size);
8 | border-radius: 50%;
9 | }
10 |
11 | .clearfix() {
12 | &:before,
13 | &:after {
14 | content: " ";
15 | display: table;
16 | }
17 | &:after {
18 | clear: both;
19 | }
20 | }
21 |
22 | .center-block() {
23 | display: block;
24 | margin-left: auto;
25 | margin-right: auto;
26 | }
27 |
28 | .text-overflow() {
29 | overflow: hidden;
30 | text-overflow: ellipsis;
31 | white-space: nowrap;
32 | }
33 |
34 | .img-responsive(@display: block) {
35 | display: @display;
36 | max-width: 100%;
37 | height: auto;
38 | }
39 |
40 | .bg-variant(@color) {
41 | background-color: @color;
42 | a&:hover {
43 | background-color: darken(@color, 10%);
44 | }
45 | }
46 | .text-emphasis-variant(@color) {
47 | color: @color;
48 | a&:hover {
49 | color: darken(@color, 10%);
50 | }
51 | }
52 |
53 | .placeholder(@color) {
54 | &::-moz-placeholder {
55 | color: @color;
56 | opacity: 1;
57 | }
58 | &:-ms-input-placeholder { color: @color; }
59 | &::-webkit-input-placeholder { color: @color; }
60 | }
61 |
62 | .tab-focus() {
63 | outline: thin dotted;
64 | outline: 5px auto -webkit-focus-ring-color;
65 | outline-offset: -2px;
66 | }
67 |
--------------------------------------------------------------------------------
/mock/city.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | // template example
3 | {
4 | // url
5 | url: '/',
6 | // Mock template
7 | data: {
8 | 'citys|1-10': {
9 | '310000': '上海市',
10 | '320000': '江苏省',
11 | '330000': '浙江省',
12 | '340000': '安徽省',
13 | '350000': '河南省'
14 | }
15 | }
16 | },
17 | // function example
18 | // 适用于需要动态切换数据的场景
19 | {
20 | url: '/:type',
21 | // Mock function
22 | // 根据参数动态切换数据
23 | // @req: Express request
24 | data(req) {
25 | const type = req.params.type
26 | if (type === 'array') {
27 | return {
28 | 'citys|1-10': [
29 | 'Mock.js'
30 | ]
31 | }
32 | }
33 | return {
34 | 'name': 'Mock.js'
35 | }
36 | }
37 | },
38 | // method example
39 | // 请求协议:get、post、put、delete,小写
40 | // 省略协议时为 get 请求
41 | {
42 | url: '/aaa',
43 | // default method is get
44 | method: 'post',
45 | data: {
46 | 'citys|2': {
47 | '310000': '上海市',
48 | '320000': '江苏省',
49 | '330000': '浙江省',
50 | '340000': '安徽省'
51 | }
52 | }
53 | }
54 | ]
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-scaffold",
3 | "version": "0.2.0",
4 | "private": true,
5 | "author": {
6 | "name": "yusen",
7 | "email": "634206017@qq.com"
8 | },
9 | "scripts": {
10 | "dev": "cross-env NODE_ENV=dev supervisor -w mock -i src build/dev-server.js",
11 | "build": "node build/build.js"
12 | },
13 | "dependencies": {
14 | "axios": "^0.15.3",
15 | "lodash": "^4.17.2",
16 | "vue": "^2.2.4",
17 | "vue-router": "^2.3.0",
18 | "vuex": "^2.2.1"
19 | },
20 | "devDependencies": {
21 | "autoprefixer": "^6.5.2",
22 | "babel-core": "^6.24.0",
23 | "babel-eslint": "^7.1.0",
24 | "babel-loader": "^6.2.4",
25 | "babel-plugin-lodash": "^3.2.10",
26 | "babel-plugin-transform-runtime": "^6.23.0",
27 | "babel-preset-es2015": "^6.24.0",
28 | "babel-preset-stage-2": "^6.22.0",
29 | "babel-runtime": "^6.23.0",
30 | "body-parser": "^1.17.1",
31 | "connect-history-api-fallback": "^1.2.0",
32 | "cross-env": "^4.0.0",
33 | "css-loader": "^0.27.3",
34 | "ejs-loader": "^0.3.0",
35 | "eslint": "^3.9.1",
36 | "eslint-config-standard": "^7.1.0",
37 | "eslint-friendly-formatter": "^2.0.6",
38 | "eslint-loader": "^1.6.1",
39 | "eslint-plugin-html": "^1.6.0",
40 | "eslint-plugin-promise": "^3.3.1",
41 | "eslint-plugin-standard": "^2.0.1",
42 | "eventsource-polyfill": "^0.9.6",
43 | "express": "^4.14.0",
44 | "extract-text-webpack-plugin": "^1.0.1",
45 | "file-loader": "^0.9.0",
46 | "html-webpack-plugin": "^2.14.0",
47 | "http-proxy-middleware": "^0.17.2",
48 | "json-loader": "^0.5.4",
49 | "less": "^2.7.1",
50 | "less-loader": "^2.2.3",
51 | "lodash-webpack-plugin": "^0.10.6",
52 | "mockjs": "^1.0.1-beta3",
53 | "multiparty": "^4.1.3",
54 | "opn": "^4.0.2",
55 | "ora": "^1.2.0",
56 | "postcss-loader": "^1.3.3",
57 | "reload": "^1.1.0",
58 | "shelljs": "^0.7.4",
59 | "url-loader": "^0.5.7",
60 | "vue-hot-reload-api": "^2.0.11",
61 | "vue-html-loader": "^1.2.2",
62 | "vue-loader": "^11.3.2",
63 | "vue-style-loader": "^2.0.4",
64 | "vue-template-compiler": "^2.2.4",
65 | "webpack": "^1.14.0",
66 | "webpack-bundle-analyzer": "^2.3.1",
67 | "webpack-dev-middleware": "^1.8.3",
68 | "webpack-hot-middleware": "^2.12.2",
69 | "webpack-merge": "^4.1.0"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-scaffold
2 |
3 | ## 介绍
4 |
5 | 基于 Webpack 的 Vue2 SPA 开发环境,支持 ES6、Less、ESlint、e2e test,种子项目已集成 vuex、vue-router、axios、mockjs 等。
6 |
7 | ## 使用
8 |
9 | ```bash
10 | # 获取脚手架到 vue-project 目录或自定义目录
11 | $ git clone git@github.com:HYFE/vue-scaffold.git vue-project
12 |
13 | # 进入工作目录或你的自定义目录
14 | $ cd vue-project
15 |
16 | # 安装依赖
17 | $ npm install
18 |
19 | # 全局安装 supervisor,监视开发服务器和 webpack 配置等代码更改
20 | $ npm install supervisor -g
21 |
22 | # 启动开发服务
23 | $ npm run dev
24 |
25 | # e2e test
26 | $ npm run e2e
27 |
28 | # 打包
29 | $ npm run build
30 | ```
31 |
32 | ## 目录结构
33 |
34 | ```bash
35 | ├── build/ # 环境配置文件
36 | │ ├── build.js
37 | │ ├── config.js # 目录及端口等常用配置项
38 | │ ├── dev-client.js
39 | │ ├── dev-server.js
40 | │ ├── mock.routes.js
41 | │ ├── webpack.base.conf.js
42 | │ ├── webpack.dev.conf.js
43 | │ ├── webpack.prod.conf.js
44 | ├── mock/
45 | │ └── ... # mock路由和数据配置
46 | ├── src/
47 | │ ├── main.js # 项目入口文件
48 | │ ├── router/ # 路由配置
49 | │ │ └── ...
50 | │ ├── App.vue # main app component
51 | │ ├── index.html # index.html
52 | │ ├── components/ # 可复用组件
53 | │ │ └── ...
54 | │ ├── less/ # less
55 | │ │ └── ...
56 | │ ├── view/ # 视图组件
57 | │ │ └── ...
58 | │ ├── libs/ # 第三方库文件
59 | │ │ └── ...
60 | │ ├── plugins/ # Vue插件
61 | │ │ └── ...
62 | │ ├── util/ # 常用工具库
63 | │ │ └── ...
64 | │ ├── assets/ # 静态资源文件
65 | │ │ └── fonts
66 | │ │ └── images
67 | │ ├── store/ # vuex
68 | │ │ ├── index.js # 组合 vuex 模块
69 | │ │ ├── actions.js # 根 actions
70 | │ │ ├── mutations.js # 根 mutations
71 | │ │ ├── modules/ # 根据业务逻辑划分子模块
72 | │ │ │ └── ...
73 | │ ├── api/
74 | │ │ └── index.js # 最终请求后端的地方
75 | ├── upload/ # 模拟文件上传目录
76 | │ └── ...
77 | ├── test/
78 | │ ├── e2e/ # e2e test
79 | │ │ ├── custom-assertions/ # 自定义断言
80 | │ │ │ └── ...
81 | │ │ ├── specs/ # 测试用例
82 | │ │ │ └── ...
83 | │ │ ├── nightwatch.conf.js # nightwatch 配置
84 | │ │ ├── runner.js # e2e start
85 | ├── dic.yml # 项目内关键字命名字典,供所有人查阅
86 | ├── .editorconfig # 编辑器配置
87 | ├── .eslintignore # eslint ignore conf
88 | ├── .eslintrc.js # eslint conf
89 | └── package.json # build scripts and dependencies
90 | ```
91 |
--------------------------------------------------------------------------------
/src/api/ajax.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { ROOT_PATH } from './config'
3 |
4 | const FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded'
5 | axios.defaults.headers.post['Content-Type'] = FORM_CONTENT_TYPE
6 | axios.defaults.headers.put['Content-Type'] = FORM_CONTENT_TYPE
7 |
8 | const Ajax = axios.create({
9 | // 公共 HTTP 配置
10 | baseURL: ROOT_PATH,
11 | // timeout: 1000,
12 | // headers: { 'X-Custom-Header': 'foobar' },
13 | responseType: 'json'
14 | })
15 |
16 | /**
17 | * 格式化参数
18 | * @param {*} params Object
19 | */
20 | const QS = (params = {}) => {
21 | let url = ''
22 | Object.keys(params).forEach((k, i) => {
23 | url += `${i === 0 ? '' : '&'}${k}=${params[k]}`
24 | })
25 | return url
26 | }
27 |
28 | const HTTP_HANDLE = req => req.then(res => res.data).catch(err => console.error('[API]:', err))
29 |
30 | /**
31 | * http GET
32 | * @param {*} url String
33 | * @param {*} params Object
34 | */
35 | export const GET = (url, params) => HTTP_HANDLE(Ajax.get(url, params ? {
36 | params
37 | } : params))
38 |
39 | /**
40 | * http POST
41 | * @param {*} url String
42 | * @param {*} body Object
43 | */
44 | export const POST = (url, body) => HTTP_HANDLE(Ajax.post(url, QS(body)))
45 |
46 | /**
47 | * http PUT
48 | * @param {*} url String
49 | * @param {*} body Object
50 | */
51 | export const PUT = (url, body) => HTTP_HANDLE(Ajax.put(url, QS(body)))
52 |
53 | /**
54 | * http DELETE
55 | * @param {*} url String
56 | * @param {*} params Object
57 | */
58 | export const DELETE = (url, params) => HTTP_HANDLE(Ajax.delete(url, params ? {
59 | params
60 | } : params))
61 |
62 | const fixUrl = url => (url.lastIndexOf('/') === url.length ? url : `${url}/`)
63 | const urlPattern = (url, arg) => ({
64 | url: url.replace(/:[a-zA-Z]+/g, m => arg.shift()),
65 | params: arg.length ? arg[0] : null
66 | })
67 |
68 | /**
69 | * 对常用 RESTful 请求格式的封装,支持多级资源请求
70 | * 以一个实体 User 为例,基础 URL 为 `/api/user`,则:
71 | * 查询 GET /api/user/:id
72 | * 添加 POST /api/user
73 | * 修改 PUT /api/user/:id
74 | * 删除 DELETE /api/user/:id
75 | *
76 | * @export
77 | * @class Api
78 | */
79 | export default class Api {
80 | constructor(url) {
81 | this.url = fixUrl(url)
82 | }
83 |
84 | add(...args) {
85 | const { url, params } = urlPattern(this.url, args)
86 | return POST(url, params)
87 | }
88 |
89 | update(...args) {
90 | const { url, params } = urlPattern(`${this.url}:id`, args)
91 | return PUT(url, params)
92 | }
93 |
94 | delete(...args) {
95 | const { url, params } = urlPattern(`${this.url}:id`, args)
96 | return DELETE(url, params)
97 | }
98 |
99 | one(...args) {
100 | const { url, params } = urlPattern(`${this.url}:id`, args)
101 | return GET(url, params)
102 | }
103 |
104 | all(...args) {
105 | const { url, params } = urlPattern(this.url, args)
106 | return GET(url, params)
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/less/reset.less:
--------------------------------------------------------------------------------
1 | html {
2 | font-family: sans-serif;
3 | -ms-text-size-adjust: 100%;
4 | -webkit-text-size-adjust: 100%;
5 | }
6 |
7 | body, h1, h2, h3, h4, h5, h6, p, figure, pre, dl, dd, blockquote {
8 | margin: 0;
9 | }
10 |
11 | input, legend, input, textarea, button {
12 | padding: 0;
13 | }
14 |
15 | ul, ol, form, fieldset, th, td {
16 | margin: 0;
17 | padding: 0;
18 | }
19 |
20 | article,
21 | aside,
22 | details,
23 | figcaption,
24 | figure,
25 | footer,
26 | header,
27 | hgroup,
28 | main,
29 | menu,
30 | nav,
31 | section,
32 | summary {
33 | display: block;
34 | }
35 |
36 | audio,
37 | canvas,
38 | progress,
39 | video {
40 | display: inline-block;
41 | vertical-align: baseline;
42 | }
43 |
44 | audio:not([controls]) {
45 | display: none;
46 | height: 0;
47 | }
48 |
49 | [hidden],
50 | template {
51 | display: none;
52 | }
53 |
54 | a {
55 | background-color: transparent;
56 | }
57 | a:active,
58 | a:hover {
59 | outline: 0;
60 | }
61 |
62 | abbr[title] {
63 | border-bottom: 1px dotted;
64 | }
65 |
66 | ul, ol {
67 | list-style: none;
68 | }
69 |
70 | b,
71 | strong {
72 | font-weight: bold;
73 | }
74 |
75 | mark {
76 | background: #ff0;
77 | color: #000;
78 | }
79 |
80 | small {
81 | font-size: 80%;
82 | }
83 |
84 | sub,
85 | sup {
86 | font-size: 75%;
87 | line-height: 0;
88 | position: relative;
89 | vertical-align: baseline;
90 | }
91 |
92 | sup {
93 | top: -0.5em;
94 | }
95 |
96 | sub {
97 | bottom: -0.25em;
98 | }
99 |
100 | img,
101 | legend {
102 | border: 0;
103 | }
104 |
105 | img, input, button, label {
106 | vertical-align: middle;
107 | }
108 |
109 | svg:not(:root) {
110 | overflow: hidden;
111 | }
112 |
113 | hr {
114 | box-sizing: content-box;
115 | height: 0;
116 | }
117 |
118 | pre {
119 | overflow: auto;
120 | }
121 |
122 | code,
123 | kbd,
124 | pre,
125 | samp {
126 | font-family: monospace, monospace;
127 | font-size: 1em;
128 | }
129 |
130 | button,
131 | input,
132 | optgroup,
133 | select,
134 | textarea {
135 | color: inherit;
136 | font: inherit;
137 | margin: 0;
138 | }
139 |
140 | button {
141 | overflow: visible;
142 | }
143 |
144 | button,
145 | select {
146 | text-transform: none;
147 | }
148 |
149 | button,
150 | html input[type="button"],
151 | input[type="reset"],
152 | input[type="submit"] {
153 | -webkit-appearance: button;
154 | cursor: pointer;
155 | }
156 |
157 | button[disabled],
158 | html input[disabled] {
159 | cursor: default;
160 | }
161 |
162 | button::-moz-focus-inner,
163 | input::-moz-focus-inner {
164 | border: 0;
165 | padding: 0;
166 | }
167 |
168 | input {
169 | line-height: normal;
170 | }
171 |
172 | input[type="number"]::-webkit-inner-spin-button,
173 | input[type="number"]::-webkit-outer-spin-button {
174 | height: auto;
175 | }
176 |
177 | input[type="search"] {
178 | -webkit-appearance: textfield;
179 | box-sizing: content-box;
180 | }
181 | input[type="search"]::-webkit-search-cancel-button,
182 | input[type="search"]::-webkit-search-decoration {
183 | -webkit-appearance: none;
184 | }
185 |
186 | fieldset {
187 | border: 1px solid #c0c0c0;
188 | margin: 0 2px;
189 | padding: 0.35em 0.625em 0.75em;
190 | }
191 |
192 | textarea {
193 | overflow: auto;
194 | }
195 |
196 | table {
197 | border-collapse: collapse;
198 | border-spacing: 0;
199 | }
200 |
--------------------------------------------------------------------------------
/src/components/tree/treeItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 | {{data.value}}
10 |
11 |
17 |
23 |
24 |
25 |
26 |
95 |
208 |
--------------------------------------------------------------------------------