├── .eslintignore
├── .browserslistrc
├── src
├── images
│ └── logo.jpg
├── views
│ ├── login
│ │ ├── login.scss
│ │ └── login.vue
│ ├── app.vue
│ ├── zrest
│ │ ├── zrest.js
│ │ └── zrest.vue
│ ├── home
│ │ ├── 404.vue
│ │ ├── home.vue
│ │ └── homeInner.vue
│ ├── slot-layout.vue
│ ├── stage3
│ │ └── stage3.vue
│ ├── stage1
│ │ └── stage1.vue
│ ├── stage2
│ │ └── stage2.vue
│ ├── themeColor
│ │ ├── changeColor.vue
│ │ └── themeColor.vue
│ └── iconfontPreview
│ │ └── iconfontPreview.vue
├── css
│ ├── override.scss
│ ├── index.scss
│ ├── defines.scss
│ ├── element-var-changed.scss
│ ├── project.scss
│ ├── flexible.scss
│ └── utils.scss
├── iconfont
│ ├── fonts
│ │ ├── my-app-icon.ttf
│ │ ├── my-app-icon.woff
│ │ ├── my-app-icon.woff2
│ │ ├── font.css
│ │ ├── _font-preview.html
│ │ ├── fonts.js
│ │ └── my-app-icon.svg
│ ├── svgs
│ │ ├── calendar.svg
│ │ ├── warning.svg
│ │ ├── success.svg
│ │ ├── bookmark.svg
│ │ └── state-beinvited.svg
│ └── css-template.njk
├── js
│ ├── utils
│ │ ├── userInfo.js
│ │ ├── loadScripts.js
│ │ ├── storageUtil.js
│ │ ├── msgDialog.js
│ │ ├── httpUtil.js
│ │ ├── frequence.js
│ │ ├── loading.js
│ │ └── domUtil.js
│ ├── $x.js
│ ├── validData.js
│ ├── loadScripts.js
│ ├── themeColorClient.js
│ └── axios.js
├── pages
│ ├── pageB.js
│ ├── login.js
│ ├── iconfontPreview
│ │ ├── entry.js
│ │ └── template.html
│ ├── themeColor.js
│ └── index.js
├── router
│ ├── spinRoute.js
│ └── routerMain.js
├── component
│ ├── debugInfo.vue
│ ├── footCode.vue
│ ├── leftMenu.vue
│ └── topHeader.vue
├── store
│ └── index.js
└── index.html
├── static
└── favicon.ico
├── postcss.config.js
├── .idea
└── codeStyles
│ └── codeStyleConfig.xml
├── .gitignore
├── .editorconfig
├── mock
├── root
│ ├── buyProducts.js
│ ├── api
│ │ ├── test_api.js
│ │ ├── getInfo.js
│ │ ├── test_data.js
│ │ └── getMenus.js
│ ├── .js
│ └── getProducts.js
├── mock文件说明.txt
├── maven.js
├── proxy80.js
├── static-config.js
├── mockClient.js
└── mock-config.js
├── .npmrc
├── config
├── app-config.js
├── serverMap.js
└── index.js
├── babel.config.js
├── .eslintrc.js
├── README.md
└── package.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/*.js
2 | config/*.js
3 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | Chrome >= 49
2 | Firefox >= 63
3 | Safari >= 12
4 | Edge >= 17
5 | iOS >= 8
6 | IE >= 10
7 |
--------------------------------------------------------------------------------
/src/images/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzsrc/vue-element-ui-scaffold-webpack4/HEAD/src/images/logo.jpg
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzsrc/vue-element-ui-scaffold-webpack4/HEAD/static/favicon.ico
--------------------------------------------------------------------------------
/src/views/login/login.scss:
--------------------------------------------------------------------------------
1 | /*IFTRUE_isDebug
2 | .cond-msg {
3 | background: #fffbc3;
4 | }
5 | FITRUE_isDebug */
6 |
--------------------------------------------------------------------------------
/src/css/override.scss:
--------------------------------------------------------------------------------
1 | @import "defines";
2 | //在这里写覆盖饿了么的全局样式
3 |
4 | .primay-color {
5 | color: $--color-primary;
6 | }
7 |
--------------------------------------------------------------------------------
/src/iconfont/fonts/my-app-icon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzsrc/vue-element-ui-scaffold-webpack4/HEAD/src/iconfont/fonts/my-app-icon.ttf
--------------------------------------------------------------------------------
/src/iconfont/fonts/my-app-icon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzsrc/vue-element-ui-scaffold-webpack4/HEAD/src/iconfont/fonts/my-app-icon.woff
--------------------------------------------------------------------------------
/src/iconfont/fonts/my-app-icon.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzsrc/vue-element-ui-scaffold-webpack4/HEAD/src/iconfont/fonts/my-app-icon.woff2
--------------------------------------------------------------------------------
/src/views/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/src/js/utils/userInfo.js:
--------------------------------------------------------------------------------
1 | export default {
2 | token: '',
3 | color: '',
4 | setUserInfo(userInfo) {
5 | Object.assign(this, userInfo)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('autoprefixer')({}),
4 | require('postcss-pxtorem')({rootValue: 100, propList: ['*']})
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | .idea
4 | dist/
5 | npm-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 | test/unit/coverage
9 | test/e2e/reports
10 | selenium-debug.log
11 |
--------------------------------------------------------------------------------
/src/css/index.scss:
--------------------------------------------------------------------------------
1 | @import "../iconfont/fonts/font.css";
2 | @import "override.scss";
3 | @import "defines.scss";
4 | @import "flexible.scss";
5 | @import "utils.scss";
6 | @import "project.scss";
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 |
--------------------------------------------------------------------------------
/mock/root/buyProducts.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | disabled: 0,
3 | body: function (query, post) {
4 | return {
5 | data: '',
6 | msg: '',
7 | status: 0
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/mock/root/api/test_api.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | disabled: 0,
3 | delay: 1000,
4 | body: {
5 | delayed: '1 seconds',
6 | data: { a: 11, b: 22, id: 1 },
7 | msg: '',
8 | status: 0
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/css/defines.scss:
--------------------------------------------------------------------------------
1 | @import "./element-var-changed.scss";
2 | //theme-chalk的变量名
3 | @import "../../node_modules/element-ui/packages/theme-chalk/src/common/var.scss";
4 |
5 | //自定义颜色变量
6 | $my-custom-color: #0cdd3a;
7 | $my-custom-color2: #c655dd;
8 |
--------------------------------------------------------------------------------
/src/views/zrest/zrest.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import zrest from './zrest.vue';
3 |
4 | require('../../css/utils.scss');
5 |
6 |
7 | new Vue({
8 | el: '#app',
9 | template: ' ',
10 | components: { zrest }
11 | })
12 |
--------------------------------------------------------------------------------
/mock/mock文件说明.txt:
--------------------------------------------------------------------------------
1 | Mock对象的字段:
2 |
3 | disabled: 1或true表示禁用mock. 此时的请求将反向代理到接口服务器(proxyTarget).
4 | status: 返回的http状态. 默认为200
5 | headers: 返回的http头. 默认为Content-Type: application/json; charset=utf-8。可省略。
6 | body: 返回的http主体. 可为json对象或字符串
7 | delay: 延时返回的毫秒数
8 |
--------------------------------------------------------------------------------
/src/iconfont/svgs/calendar.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/mock/maven.js:
--------------------------------------------------------------------------------
1 | // 实现maven仓库转发
2 |
3 | const config = {
4 | mockEnabled: false,
5 | proxyTarget: function (urlPart) {
6 | return 'https://maven.aliyun.com/repository/public'
7 | },
8 | isHttps: false, // 是否https
9 | port: 8099 // 端口
10 | }
11 | module.exports = config
12 |
--------------------------------------------------------------------------------
/src/views/home/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
11 |
12 |
15 |
--------------------------------------------------------------------------------
/mock/proxy80.js:
--------------------------------------------------------------------------------
1 | // 实现将本地80端口代理到9998端口,配合hosts配置,用于调试微信(因为微信必须使用域名)
2 |
3 | const config = {
4 | mockEnabled: false,
5 | proxyTarget: function (urlPart) {
6 | return 'http://localhost:8090'
7 | },
8 | isHttps: false, // 是否https
9 | port: 80 // 端口
10 | }
11 | module.exports = config
12 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://r.cnpmjs.org
2 | sass_binary_site=https://npmmirror.com/mirrors/node-sass/
3 | phantomjs_cdnurl=https://npmmirror.com/mirrors/phantomjs
4 | ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
5 | chromedriver_cdnurl=http://npm.taobao.org/mirrors/chromedrive
6 | puppeteer_download_host=https://npmmirror.com/mirrors
7 |
--------------------------------------------------------------------------------
/src/views/slot-layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/src/pages/pageB.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | require('../css/index.scss');
3 | new Vue({
4 | el: '#app',
5 | template:
6 | '
\n' +
7 | '
Multipage sample \n' +
8 | '
This is pageB here \n' +
9 | '
Back \n' +
10 | '
',
11 | components: {}
12 | })
13 |
14 |
--------------------------------------------------------------------------------
/mock/root/.js:
--------------------------------------------------------------------------------
1 | //本文件模拟根目录的响应数据(类似index.html的作用)
2 | var fs = require('fs')
3 | module.exports = {
4 | disabled: 1,
5 | headers: {
6 | 'Content-Type': 'text/html'
7 | },
8 | body: function (query, post) {
9 | return `
10 |
11 |
12 | Hi, dynamic-mocker is running.
13 |
14 |
15 | `
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/mock/static-config.js:
--------------------------------------------------------------------------------
1 | // 将一个目录作为http服务启动
2 |
3 | const config = {
4 | mockEnabled: false, // 设置mock失效,使用proxy功能
5 | proxyTarget: false, // 设置proxy失效,使用static功能
6 | static: {
7 | index: 'index.html',
8 | path: '../dist'
9 | },
10 | isHttps: false, // 是否https
11 | port: 8061 // 端口
12 | }
13 | module.exports = config
14 |
--------------------------------------------------------------------------------
/src/pages/login.js:
--------------------------------------------------------------------------------
1 | //Login page
2 |
3 | import Vue from 'vue';
4 | import login from '../views/login/login.vue'
5 | import debugInfo from '../component/debugInfo';
6 |
7 | require('../css/index.scss');
8 |
9 | //调试信息组件
10 | Vue.use(debugInfo)
11 |
12 | new Vue({
13 | el: '#app',
14 | template: ' ',
15 | components: { login }
16 | })
17 |
18 |
--------------------------------------------------------------------------------
/mock/root/api/getInfo.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | disabled: 0,
3 | status: 200,
4 | body: function (query, post) {
5 | return {
6 | status: 0,
7 | msg: '',
8 | data: {
9 | name: '张三',
10 | avatar: '',
11 | token: +new Date()
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/css/element-var-changed.scss:
--------------------------------------------------------------------------------
1 | //可在本文件中覆盖-theme-chalk同名变量
2 | //会将此文件附加在 "~element-ui/packages/theme-chalk/src/common/var.scss" 之前(通过join-file-content-plugin实现);
3 |
4 | //覆盖主色
5 | $--color-primary: #f67a17; //去掉 !default
6 |
7 | $--color-primary-light-95: mix(#fff, $--color-primary, 95%);
8 | $--color-primary-dark-1: mix(#000, $--color-primary, 10%);
9 | $--color-primary-dark-2: mix(#000, $--color-primary, 20%);
10 | $--color-primary-dark-7: mix(#000, $--color-primary, 70%);
11 |
--------------------------------------------------------------------------------
/src/pages/iconfontPreview/entry.js:
--------------------------------------------------------------------------------
1 | import iconfontPreview from '../../views/iconfontPreview/iconfontPreview';
2 | import Vue from 'vue';
3 | require('../../css/index.scss');
4 |
5 |
6 | new Vue({
7 | el: '#app',
8 | template: `
9 |
10 |
Iconfont demo. You can modify or add a svg file in [src/iconfont/svgs], and see it refreshing.
11 |
12 | `,
13 | components: { iconfontPreview }
14 | })
15 |
--------------------------------------------------------------------------------
/src/pages/themeColor.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import $x from '../js/$x'
3 | import themeColor from '../views/themeColor/themeColor.vue'
4 | import { initThemeColor } from '../js/themeColorClient'
5 |
6 | require('../css/index.scss');
7 |
8 | // window.Vue = Vue
9 |
10 | Vue.prototype.$ELEMENT = { size: 'small' }
11 |
12 | // 通用组件,便于处理
13 | Vue.prototype.$x = Vue.$x = $x;
14 |
15 | initThemeColor()
16 | new Vue({
17 | el: '#app',
18 | render: h => h(themeColor),
19 | });
20 |
21 |
--------------------------------------------------------------------------------
/mock/root/getProducts.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | disabled: 0,
3 | body: function (query, post) {
4 | return {
5 | data: [
6 | { id: 1, title: 'iPad 4 Mini', price: 500.01, inventory: 2 },
7 | { id: 2, title: 'H&M T-Shirt White', price: 10.99, inventory: 10 },
8 | { id: 3, title: 'Charli XCX - Sucker CD', price: 19.99, inventory: 5 }
9 | ],
10 | msg: '',
11 | status: 0
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/config/app-config.js:
--------------------------------------------------------------------------------
1 | var getElementUISeries = require('webpack-theme-color-replacer/forElementUI/getElementUISeries')
2 |
3 | module.exports = {
4 | LOGIN_PATH: './',
5 | title: 'vue + webpack4 + element-ui脚手架项目',
6 | description: 'vue + webpack4 + element-ui脚手架项目',
7 |
8 | themeColor: '#f67a17',
9 | otherColors: ['#10213a', '#fefeff'], //titleBg、titleFg
10 | getThemeColors: function (primaryColor, otherColors) {
11 | return getElementUISeries(primaryColor, otherColors);//element-ui主色系列
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/mock/mockClient.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | import mockClient from 'dynamic-mocker/lib/client.js'
4 |
5 | var config = require('./mock-config.js')
6 |
7 | mockClient.setup(config, {
8 | '/api/getInfo': require('./root/api/getInfo.js'),
9 | '/api/getMenus': require('./root/api/getMenus.js'),
10 | '/api/test_api': require('./root/api/test_api.js'),
11 | '/api/test_data': require('./root/api/test_data.js'),
12 | '/buyProducts': require('./root/buyProducts.js'),
13 | '/getProducts': require('./root/getProducts.js')
14 | })
15 |
--------------------------------------------------------------------------------
/src/router/spinRoute.js:
--------------------------------------------------------------------------------
1 | /*
2 | 使vue-router懒加载时可以显示一个加载提示,避免网速慢时无响应
3 | 用法:
4 | const route = {
5 | path: '/page-layout',
6 | component: () => spinRoute.require(import('./pageLayout/pageLayout.vue'))
7 | };
8 | */
9 |
10 | 'user strict';
11 | import loading from '../js/utils/loading';
12 |
13 | export default {
14 | require(componentPromise) {
15 | loading.show();
16 | return componentPromise.then(component => {
17 | loading.close();
18 | return component
19 | })
20 | },
21 | };
22 |
--------------------------------------------------------------------------------
/src/views/stage3/stage3.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is stage3 under stage2 here
4 |
5 | Back
6 |
7 |
8 |
9 |
24 |
25 |
28 |
--------------------------------------------------------------------------------
/src/views/stage1/stage1.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
25 |
--------------------------------------------------------------------------------
/src/iconfont/svgs/warning.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | warning1
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/iconfont/svgs/success.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | success
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/component/debugInfo.vue:
--------------------------------------------------------------------------------
1 |
11 |
24 |
--------------------------------------------------------------------------------
/src/iconfont/svgs/bookmark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Path 2
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/js/utils/loadScripts.js:
--------------------------------------------------------------------------------
1 | export default function loadScripts(urls, callback) {
2 | if (typeof urls === 'string') urls = [urls]
3 | let loaded = 0
4 | urls.map(url => {
5 | if (document.querySelector('script[src="' + url + '"]')) {
6 | onScriptLoad()
7 | }
8 | else {
9 | const script = document.createElement('script')
10 | script.src = url
11 | script.onload = onScriptLoad
12 | document.querySelector('head').appendChild(script)
13 | }
14 | })
15 |
16 | function onScriptLoad() {
17 | loaded++
18 | if (loaded === urls.length) {
19 | if (callback) callback()
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/css/project.scss:
--------------------------------------------------------------------------------
1 | // 项目的公告样式
2 |
3 | .global-mask {
4 | background: transparent;
5 | }
6 |
7 | /*滚动条样式*/
8 | ::-webkit-scrollbar {
9 | border-radius: 8px;
10 | width: 8px;
11 | height: 8px;
12 | }
13 |
14 | //::-webkit-scrollbar-track {
15 | // border-radius: 8px;
16 | // -webkit-box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.3);
17 | // background: linear-gradient(90deg, #efefef 0%, #fff 78%, #efefef 100%);
18 | //}
19 |
20 | ::-webkit-scrollbar-thumb {
21 | border-radius: 8px;
22 | -webkit-box-shadow: inset 0 0 3px rgba(0, 0, 0, .3);
23 | background: linear-gradient(90deg, #efefef 0%, #fff 78%, #efefef 100%);
24 | }
25 |
26 | ::-webkit-scrollbar-thumb:vertical {
27 | background: linear-gradient(0deg, #efefef 0%, #fff 78%, #efefef 100%);
28 | }
29 |
--------------------------------------------------------------------------------
/src/views/stage2/stage2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is stage2 here. Go stage3
4 |
5 |
Back
6 |
7 |
8 |
9 |
10 |
11 |
22 |
23 |
38 |
--------------------------------------------------------------------------------
/src/js/$x.js:
--------------------------------------------------------------------------------
1 | import StorageUtil from './utils/storageUtil.js'
2 | import userInfo from './utils/userInfo.js'
3 | import domUtil from './utils/domUtil.js'
4 | import msgDialog from './utils/msgDialog.js'
5 | import httpUtil from './utils/httpUtil.js'
6 | //日期格式化
7 | import formatDate from 'date-any/formatDate.js'
8 |
9 | const util = {
10 | clone(obj, deep) {
11 | if (!obj) return obj;
12 | if (deep) return JSON.parse(JSON.stringify(obj));
13 | return Array.isArray(obj) ? obj.slice(0) : Object.assign({}, obj)
14 | },
15 | noop() {
16 | }
17 | }
18 |
19 | const mixed = {
20 | ...util,
21 | ...msgDialog,
22 | ...httpUtil,
23 | storage: new StorageUtil(),
24 | userInfo,
25 | formatDate
26 | }
27 |
28 | Object.assign(domUtil, mixed)
29 |
30 | export default domUtil
31 |
--------------------------------------------------------------------------------
/src/component/footCode.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
25 |
26 |
--------------------------------------------------------------------------------
/src/js/utils/storageUtil.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class StorageUtil {
4 | constructor(storage) {
5 | this.storage = storage || localStorage
6 | // 缓存
7 | this.caches = {}
8 | }
9 |
10 | getObj(name) {
11 | const r = this.caches[name]
12 | if (r !== undefined) return r
13 | try {
14 | const str = this.storage.getItem(name);
15 | return str && JSON.parse(str);
16 | } catch (e) {
17 | console.warn(e)
18 | }
19 | }
20 |
21 | setObj(name, obj) {
22 | this.caches[name] = obj
23 | if (obj === undefined) {
24 | this.storage.removeItem(name);
25 | } else {
26 | this.storage.setItem(name, JSON.stringify(obj));
27 | }
28 | }
29 | }
30 |
31 | export default StorageUtil
32 |
--------------------------------------------------------------------------------
/config/serverMap.js:
--------------------------------------------------------------------------------
1 | var configs = {
2 | //本地开发环境的接口地址(npm run dev)
3 | dev: {
4 | base: '//localhost:8087', //本地开发调试用的服务器地址,修改不会影响发布
5 | node_api: '//127.0.0.1:7001', //本地开发调试用的服务器地址,修改不会影响发布
6 | },
7 | //待发布的开发环境的接口地址(npm run build-dev)
8 | dev_build: {
9 | base: '//xxx.dev61.xxx.com',
10 | node_api: '//xxx-node.dev61.xxx.com',
11 | },
12 | //测试环境的接口地址
13 | test: {
14 | base: '//aaa-service-test.xxx.com',
15 | node_api: '//bbb-node-service-test.xxx.com',
16 | },
17 | //演示环境的接口地址
18 | demo: {
19 | base: '//aaa-demo.xxx.com',
20 | node_api: '//bbb-node-service-demo.xxx.com'
21 | },
22 | //生产环境的接口地址
23 | prod: {
24 | base: '//aaa.xxx.com',
25 | node_api: '//bbb-node-service.xxx.com',
26 | }
27 | }
28 | module.exports = configs[process.env.ENV_CONFIG]
29 |
30 |
--------------------------------------------------------------------------------
/src/js/validData.js:
--------------------------------------------------------------------------------
1 | /*
2 | 输入数据校验组件
3 | */
4 | export default {
5 | checkMobile(rule, value, callback) {
6 | if (!value) {
7 | return callback(new Error('请输入手机号码'));
8 | } else if (!(/^1[345678]\d{9}$/.test(value))) {
9 | return callback(new Error('手机号格式不正确'));
10 | } else {
11 | return callback();
12 | }
13 | },
14 | checkIdCard(rule, value, callback) {
15 | const reg = /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
16 | if (!value) {
17 | return callback();
18 | } else if (!(reg.test(value))) {
19 | return callback(new Error('请输入正确的公民身份证号'));
20 | } else {
21 | return callback();
22 | }
23 | },
24 | checkEmail(value) {
25 | return /^[\w\.\-]*\w@[\w\.\-]+\.[\w\.\-]+$/.test(value)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/mock/root/api/test_data.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | disabled: 0,
3 | delay: 200,
4 | status: 200,
5 | headers: {
6 | server: 'dynamic-mocker',
7 | 'set-cookie': 'foo=bar; path=/',
8 | 'cache-control': 'no-cache'
9 | },
10 | body: function (query, post) {
11 | const pageIndex = post.pageIndex
12 | const pageSize = post.pageSize
13 | return {
14 | status: 0,
15 | data: {
16 | datas: new Array(pageSize).fill().map((n, i) => {
17 | const id = pageIndex * pageSize + i
18 | return {
19 | id: id,
20 | name: '张三' + id,
21 | date: +new Date() - i * 100000
22 | }
23 | }),
24 | total: 111,
25 | },
26 | msg: 'ok'
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/js/utils/msgDialog.js:
--------------------------------------------------------------------------------
1 | //为element-ui的Message添加默认参数
2 | import { Loading, Message, MessageBox } from 'element-ui'
3 |
4 | function toastCtor(key, options) {
5 | const defaultOptions = {
6 | showClose: true,
7 | duration: 4000,
8 | message: options,
9 | }
10 | const fn = key ? Message[key] : Message;
11 | return fn(Object.assign(defaultOptions, options))
12 | }
13 |
14 | const toast = Object.assign(toastCtor.bind(null, null), Message);
15 | ['success', 'warning', 'info', 'error'].forEach(key => {
16 | toast[key] = toastCtor.bind(null, key)
17 | })
18 |
19 | //为element-ui的MessageBox添加默认参数
20 | MessageBox.setDefaults({ closeOnClickModal: false, closeOnPressEscape: true });
21 |
22 | export default {
23 | msgBox: MessageBox,
24 | alert: MessageBox.alert,
25 | confirm: MessageBox.confirm,
26 | prompt: MessageBox.prompt,
27 | toast: toast,
28 | loading: Loading.service
29 | }
30 |
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import $x from '../js/$x'
3 | import router from '../router/routerMain.js'
4 | import store from '../store/index.js'
5 | import main from '../views/app.vue'
6 | import { Button, Table, TableColumn, Pagination } from 'element-ui'
7 | import { initThemeColor } from '../js/themeColorClient'
8 | import debugInfo from '../component/debugInfo.vue'
9 |
10 | require('../css/index.scss');
11 |
12 | // 仅对 npm run build-preview 时,使用客户端mock数据(拦截XMLHttpRequest)
13 | /* IFTRUE_isPreview */
14 | require('../../mock/mockClient')
15 | /* FITRUE_isPreview */
16 |
17 | Vue.prototype.$ELEMENT = { size: 'small' }
18 |
19 | // 通用组件,便于处理
20 | Vue.prototype.$x = Vue.$x = $x;
21 |
22 | // 常用组件在这注册。即可实现按需加载,又不必每个页面调用Vue.use。
23 | Vue.use(Button).use(Table).use(TableColumn).use(Pagination);
24 |
25 | //调试信息组件
26 | Vue.use(debugInfo)
27 |
28 | initThemeColor()
29 | new Vue({
30 | el: '#app',
31 | router,
32 | store,
33 | render: h => h(main),
34 | });
35 |
36 | /* IFDEBUG
37 | window.$x = $x
38 | IFDEBUG */
39 |
--------------------------------------------------------------------------------
/src/js/utils/httpUtil.js:
--------------------------------------------------------------------------------
1 | import axios from '../axios';
2 |
3 | // 统一axios的get、post等调用方式
4 | export function createHttp(axios) {
5 | const httpUtil = {
6 | axios,
7 | get(url, pars, config) {
8 | return axios.get(toQueryUrl(url, pars), config)
9 | },
10 | };
11 |
12 | ['post', 'put'].map(method => {
13 | httpUtil[method] = (url, pars, config) => {
14 | return axios[method](url, pars, config)
15 | }
16 | })
17 | httpUtil.delete = (url, pars, config) => {
18 | return axios.delete(url, pars && { data: pars }, config)
19 | }
20 | httpUtil.toQueryUrl = toQueryUrl;
21 | return httpUtil
22 | }
23 |
24 | export default createHttp(axios);
25 |
26 | function toQueryUrl(rawUrl, pars) {
27 | if (pars) {
28 | pars.f_rnd = +new Date(); //防止火狐缓存GET请求
29 | rawUrl += rawUrl.indexOf('?') > -1 ? '&' : '?';
30 | rawUrl += Object.keys(pars).map(key => key + '=' + encodeURIComponent(pars[key])).join('&');
31 | }
32 | return rawUrl
33 | };
34 |
--------------------------------------------------------------------------------
/src/iconfont/fonts/font.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: my-app-icon;
3 | src: url("my-app-icon.woff2?948e5646") format("woff2"),
4 | url("my-app-icon.woff?948e5646") format("woff"),
5 | url("my-app-icon.ttf?948e5646") format("truetype"),
6 | url("my-app-icon.svg?948e5646#my-app-icon") format("svg");
7 | font-weight: normal;
8 | font-style: normal;
9 | }
10 |
11 | [class^="my-icon"], [class*=" my-icon"] {
12 | font-family: 'my-app-icon';
13 | -webkit-font-smoothing: antialiased;
14 | -moz-osx-font-smoothing: grayscale;
15 | font-style: normal;
16 | }
17 |
18 |
19 | .my-icon-bookmark::before {
20 | content: "\12c";
21 | }
22 |
23 | .my-icon-calendar::before {
24 | content: "\12d";
25 | }
26 |
27 | .my-icon-state-beinvited::before {
28 | content: "\12e";
29 | }
30 |
31 | .my-icon-success::before {
32 | content: "\12f";
33 | }
34 |
35 | .my-icon-warning::before {
36 | content: "\130";
37 | }
38 |
39 | .my-icon-pwd_msk::before {
40 | content: "\7f";
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/src/js/loadScripts.js:
--------------------------------------------------------------------------------
1 | export function loadScripts(urls, callback) {
2 | if (typeof urls === 'string') urls = [urls]
3 | if (urls.length === 0) {
4 | if (callback) callback()
5 | return
6 | }
7 | let loaded = 0
8 | const loadedIndex = {}
9 | urls.map((url, index) => {
10 | if (document.querySelector('script[src="' + url + '"]')) {
11 | onScriptLoad(index)
12 | } else {
13 | const script = document.createElement('script')
14 | script.setAttribute('src', url)
15 | const cb = onScriptLoad.bind(script, index)
16 | script.onload = cb
17 | script.onerror = cb
18 | document.querySelector('head').appendChild(script)
19 | }
20 | })
21 |
22 | function onScriptLoad(index) {
23 | if (!loadedIndex[index]) {
24 | loaded++
25 | loadedIndex[index] = 1
26 |
27 | if (loaded === urls.length) {
28 | if (callback) callback()
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | corejs: 3,
7 | useBuiltIns: 'usage',
8 | modules: false,
9 | //开发环境,不做es6转换,以便于调试
10 | targets: process.env.NODE_ENV === 'development' ? 'fully supports es6' : 'browserslist config'
11 | }
12 | ],
13 | '@vue/babel-preset-jsx', // for babel7
14 | ],
15 | plugins: [
16 | //'@babel/plugin-syntax-dynamic-import',
17 | //'@babel/plugin-transform-runtime', //用了useBuiltIns不需要它
18 | //['@babel/plugin-proposal-class-properties'],
19 | //babel7不用这个:'@vue/babel-plugin-transform-vue-jsx',
20 | [
21 | 'babel-plugin-component',
22 | {
23 | libraryName: 'element-ui',
24 | styleLibraryName: '~node_modules/element-ui/packages/theme-chalk/src',
25 | ext: '.scss'
26 | }
27 | ]
28 | ],
29 | comments: false,
30 | }
31 |
--------------------------------------------------------------------------------
/src/js/utils/frequence.js:
--------------------------------------------------------------------------------
1 | export default {
2 | //频率控制 函数连续调用时,fn 执行频率限定为 1次/waitMs。立即执行
3 | throttle: function throttle (waitMs, fn) {
4 | let lastRun = 0;
5 | return function () {
6 | const now = +new Date();
7 | if (now - lastRun > waitMs) {
8 | lastRun = now;
9 | fn.apply(null, arguments);
10 | }
11 | }
12 | },
13 | //空闲控制 返回函数连续调用时,空闲时间必须大于或等于 waitMs,fn 才会执行。延迟执行
14 | debounce: function debounce (waitMs, fn) {
15 | let lastCall, args, timeout;
16 | return function r () {
17 | lastCall = +new Date();
18 | args = arguments;
19 | if (!timeout) {
20 | timeout = setTimeout(later, waitMs);
21 | }
22 | };
23 |
24 | function later () {
25 | const past = +new Date() - lastCall;
26 | if (past > waitMs) {
27 | timeout = null;
28 | fn.apply(null, args)
29 | }
30 | else {
31 | timeout = setTimeout(later, waitMs - past + 1)
32 | }
33 | }
34 | }
35 | };
--------------------------------------------------------------------------------
/src/js/themeColorClient.js:
--------------------------------------------------------------------------------
1 | import client from 'webpack-theme-color-replacer/client'
2 | import themeUtil from 'webpack-theme-color-replacer/themeUtil'
3 |
4 | import appConfig from '../../config/app-config.js'
5 |
6 | export let curColor = appConfig.themeColor
7 |
8 | // 动态切换主题色
9 | export function changeThemeColor(newColor) {
10 | var customB = parseInt(Math.random() * 256).toString(16); // 按你需要生成颜色
11 | if (customB.length == 1) customB = '0' + customB
12 | const options = {
13 | newColors: themeUtil.getMyColors(newColor, ['#88' + customB + customB, '#' + customB + '88' + customB]),
14 | }
15 | return client.changer.changeColor(options, Promise)
16 | .then(t => {
17 | curColor = newColor
18 | localStorage.setItem('theme_color', curColor)
19 | });
20 | }
21 |
22 | export function initThemeColor() {
23 | const savedColor = localStorage.getItem('theme_color')
24 | if (savedColor) {
25 | document.body.style.display = 'none'
26 | curColor = savedColor;
27 | changeThemeColor(savedColor).finally(() => {
28 | document.body.style.display = ''
29 | });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/pages/iconfontPreview/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
25 |
26 |
27 |
28 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/views/themeColor/changeColor.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
36 |
41 |
46 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | //import productList from './modules/productList'
4 |
5 | Vue.use(Vuex)
6 |
7 | // state
8 | const state = {
9 | topMenus: [],
10 | topMenuIndex: 0,
11 | }
12 |
13 | // getters
14 | const getters = {
15 | leftMenus: (state, getters) => {
16 | const topMenu = state.topMenus[state.topMenuIndex]
17 | return (topMenu && topMenu.children) || []
18 | },
19 | }
20 |
21 | // actions
22 | const actions = {
23 | setTopMenus({ commit }, { topMenus, vm }) {
24 | commit('SET_TOPMENUS', topMenus)
25 | var path = vm.$route.path
26 | var index = topMenus.findIndex(menu => {
27 | if (menu.url === path) return true
28 | if (menu.children) return menu.children.find(m => m.url === path)
29 | })
30 | if (index > -1) commit('SET_TOPMENUINDEX', index)
31 | },
32 | setTopMenuIndex({ commit }, index) {
33 | commit('SET_TOPMENUINDEX', index);
34 | },
35 | };
36 |
37 | // mutations
38 | const mutations = {
39 | SET_TOPMENUS(state, topMenus) {
40 | state.topMenus = topMenus
41 | },
42 | SET_TOPMENUINDEX(state, index) {
43 | state.topMenuIndex = index
44 | },
45 | }
46 |
47 | export default new Vuex.Store({
48 | namespaced: true,
49 | state,
50 | getters,
51 | actions,
52 | mutations,
53 | //modules: {
54 | //productList,
55 | //},
56 | })
57 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | // see http://vuejs-templates.github.io/webpack for documentation.
2 | var path = require('path')
3 |
4 | function getSourceMapPath() {
5 | // 根据安全级别,改成只有开发者知道的文件夹名或动态加密算法生成。(npm run show-map查看)
6 | // 这样既可在需要时进行手动添加源码映射方便调试,又可避免了源码泄露。
7 | var projName = path.basename(process.cwd())
8 | var md5 = require('crypto').createHash('md5');//定义加密方式:md5不可逆,此处的md5可以换成任意hash加密的方法名称;
9 | md5.update('my-' + projName);
10 | return '_map' + md5.digest('hex'); //加密后的值d
11 | }
12 |
13 | var ret = {
14 | build: {
15 | assetsRoot: path.resolve(__dirname, '../dist'),
16 | assetsSubDirectory: '',
17 | assetsPublicPath: '', // 使用相对路径,可不受路径层次限制
18 | productionSourceMap: true,
19 | sourceMapPath: getSourceMapPath(),
20 | },
21 | dev: {
22 | port: 8090,
23 | autoOpenBrowser: true,
24 | assetsSubDirectory: '',
25 | assetsPublicPath: '/',
26 | // proxyTable: {
27 | // '/api': {
28 | // target: 'http://localhost:3000',
29 | // changeOrigin: true,
30 | // pathRewrite: {'^/api': ''}
31 | // }
32 | // },
33 | // CSS Sourcemaps off by default because relative paths are "buggy"
34 | // with this option, according to the CSS-Loader README
35 | // (https://github.com/webpack/css-loader#sourcemaps)
36 | // In our experience, they generally work as expected,
37 | // just be aware of this issue when enabling this option.
38 | cssSourceMap: true
39 | }
40 | }
41 |
42 | module.exports = ret;
43 |
--------------------------------------------------------------------------------
/src/js/utils/loading.js:
--------------------------------------------------------------------------------
1 | import { Loading } from 'element-ui';
2 |
3 | //const MASK_DELAY = 5000;
4 | const DefaultMaskOptions = { customClass: 'global-mask', target: 'html > body' }
5 |
6 | const loading = {
7 | show(options) {
8 | if (!this.unique) {
9 | this.unique = Loading.service(options);
10 | }
11 | },
12 | showMask() {
13 | const mask = document.querySelector('.global-mask');
14 | if (mask) {
15 | mask.classList.remove('global-mask');
16 | }
17 | },
18 | close() {
19 | setTimeout(() => {
20 | if (this.unique) {
21 | this.unique.close();
22 | this.unique = null;
23 | }
24 | }, 100)
25 | },
26 | }
27 |
28 | export default {
29 | //计数器,防止出现多个
30 | count: 0,
31 | show(options) {
32 | if (options !== false) {
33 | options = Object.assign({}, DefaultMaskOptions, options)
34 | try {
35 | this.count++;
36 | loading.show(options);
37 | //延时显示loading蒙板
38 | //if (this.timer) clearTimeout(this.timer);
39 | //this.timer = setTimeout(() => loading.showMask(), MASK_DELAY);
40 | } catch (e) {
41 | }
42 | }
43 | },
44 | close(options) {
45 | try {
46 | if (options !== false) {
47 | if (this.count > 0) this.count--;
48 | if (this.count === 0) {
49 | //if (this.timer) clearTimeout(this.timer)
50 | loading.close()
51 | }
52 | }
53 | } catch (e) {
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/mock/mock-config.js:
--------------------------------------------------------------------------------
1 | var frontFiles = /^(\/\w+|)\/(h5\/.+|$|\w+\.html|favicon\.ico|__webpack_hmr|.+hot-update.+)/
2 |
3 | const config = {
4 | mockEnabled: true,
5 | mockPath: ['root', 'root-old'], //模拟文件根目录
6 | proxyTarget(uri) { //后台接口服务地址(代理目标),为空表示不代理
7 | var frontPart = frontFiles.exec(uri.pathname)
8 | if (frontPart) {
9 | if (frontPart && frontPart[1]) { //url带了虚拟目录,转前端时不要虚拟目录
10 | uri.pathname = frontPart[2]
11 | uri.setChanged();
12 | }
13 | //前端页面,到h5
14 | return 'http://localhost:8090'
15 | }
16 | //非前端页面(ajax和v9页面相关)
17 | return 'http://localhost:8080' //.后端
18 | },
19 | isHttps: false, //是否https
20 | port: 8087, //端口
21 | proxyOptions: {
22 | changeOrigin: true, //支持用IP远程访问
23 | },
24 | checkPath: function (urlPath) { //urlPath校验函数,返回true表示需要进行mock处理,为false直接走代理
25 | return true
26 | },
27 | beforeResponse: function (respData, req) { //数据返回前的回调钩子,respData包含status、headers、body属性
28 | respData.headers['access-control-allow-origin'] = req.headers.origin || req.headers.Origin || '';
29 | respData.headers['access-control-allow-credentials'] = 'true';
30 | respData.headers['access-control-allow-headers'] = req.headers['access-control-request-headers'] || req.headers['Access-Control-Request-Headers'] || '';
31 | respData.headers['access-control-max-age'] = '6000';
32 | respData.headers['access-control-allow-methods'] = 'PUT,POST,GET,DELETE,PATCH,OPTIONS';
33 |
34 | respData.headers.P3P = 'CP="CAO PSA OUR"';
35 | },
36 | mapFile(pathname, req) {
37 | return pathname
38 | },
39 | // genClientJs: '../src/js/mockClient.js', // 生成mockClient.js
40 | samePreview: false, // true - mock预览时disabled开关也生效(默认false,预览时忽略所有开关)
41 | logData: true, // mock预览时打印模拟数据
42 | title: 'My App'
43 | }
44 | module.exports = config;
45 |
--------------------------------------------------------------------------------
/src/views/home/home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
42 |
43 |
66 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
25 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
48 |
Loading...
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/src/router/routerMain.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import spinRoute from './spinRoute';
4 |
5 | //同步加载,合并打包
6 | import home from '../views/home/home.vue';
7 |
8 | Vue.use(Router)
9 |
10 | //组件懒加载,下载也页面组件js时显示spin状态
11 | const stage3 = () => spinRoute.require(import('../views/stage3/stage3.vue'));
12 |
13 | const router = new Router({
14 | mode: 'hash',
15 | routes: [
16 | {
17 | path: '/',
18 | component: home, //sync
19 | children: [
20 | { path: '', component: () => spinRoute.require(import('../views/home/homeInner.vue')) },
21 | { path: '/theme', component: () => spinRoute.require(import('../views/themeColor/themeColor.vue')) },
22 | { path: '/stage1', component: () => spinRoute.require(import('../views/stage1/stage1.vue')) },
23 | {
24 | path: '/stage2',
25 | component: () => spinRoute.require(import('../views/stage2/stage2.vue')),
26 | children: [
27 | {
28 | path: '/stage2/stage3',
29 | component: stage3
30 | },
31 | ]
32 | },
33 | {
34 | path: '/icons',
35 | component: () => spinRoute.require(import('../views/iconfontPreview/iconfontPreview.vue'))
36 | },
37 | { path: '/zrest', component: () => spinRoute.require(import('../views/zrest/zrest.vue')) },
38 | {
39 | path: '/layout',
40 | component: () => spinRoute.require(import('../views/slot-layout.vue'))
41 | },
42 | { path: '*', component: () => spinRoute.require(import('../views/home/404.vue')) }
43 | ],
44 | },
45 | ],
46 | })
47 | /*IFDEBUG
48 | //禁用重复警告
49 | 'push,replace'.split(',').map(method => router[method] = function () {
50 | return Router.prototype[method].apply(this, arguments).catch(t => 0)
51 | })
52 | FIDEBUG*/
53 | export default router;
54 |
--------------------------------------------------------------------------------
/src/component/leftMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | {{menu.title}}
7 |
8 |
9 |
10 |
11 |
53 |
54 |
72 |
--------------------------------------------------------------------------------
/src/views/login/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Hi {{name}}, here is the entry
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | /*IFDEBUG
14 |
15 |
16 | This message is only shown while process.env.NODE_ENV === 'development'.
17 |
18 |
19 |
20 | FIDEBUG*/
21 |
22 |
23 |
24 |
25 |
26 |
27 |
51 |
73 |
--------------------------------------------------------------------------------
/src/iconfont/css-template.njk:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: {{ fontName }};
3 | {% if formats.indexOf('eot')>-1 -%}
4 | src: url("{{ cssFontPath }}{{ fontName }}.eot");
5 | {%- endif -%}
6 | {%- set eotIndex = formats.indexOf('eot') -%}
7 | {%- set woff2Index = formats.indexOf('woff2') -%}
8 | {%- set woffIndex = formats.indexOf('woff') -%}
9 | {%- set ttfIndex = formats.indexOf('ttf') -%}
10 | {%- set svgIndex = formats.indexOf('svg') -%}
11 |
12 | src: {% if eotIndex != -1 -%}
13 | url("{{ cssFontPath }}{{ fontName }}.eot?{{ fileMark }}#iefix") format("embedded-opentype")
14 | {%- set nothing = formats.splice(eotIndex, 1) -%}
15 | {%- if formats.length != 0 -%},
16 | {% else -%}; {% endif -%}
17 | {%- endif -%}
18 | {%- if woff2Index != -1 -%}
19 | url("{{ cssFontPath }}{{ fontName }}.woff2?{{ fileMark }}") format("woff2")
20 | {%- set nothing = formats.splice(woff2Index, 1) -%}
21 | {%- if formats.length != 0 -%},
22 | {% else -%}; {% endif -%}
23 | {%- endif -%}
24 | {%- if woffIndex != -1 -%}
25 | url("{{ cssFontPath }}{{ fontName }}.woff?{{ fileMark }}") format("woff")
26 | {%- set nothing = formats.splice(woffIndex, 1) -%}
27 | {%- if formats.length != 0 -%},
28 | {% else -%}; {% endif -%}
29 | {%- endif -%}
30 | {%- if ttfIndex != -1 -%}
31 | url("{{ cssFontPath }}{{ fontName }}.ttf?{{ fileMark }}") format("truetype")
32 | {%- set nothing = formats.splice(ttfIndex, 1) -%}
33 | {%- if formats.length != 0 -%},
34 | {% else -%}; {% endif -%}
35 | {%- endif -%}
36 | {%- if svgIndex != -1 -%}
37 | url("{{ cssFontPath }}{{ fontName }}.svg?{{ fileMark }}#{{ fontName }}") format("svg");
38 | {%- endif %}
39 | font-weight: normal;
40 | font-style: normal;
41 | }
42 |
43 | [class^="{{ cssPrefix }}"], [class*=" {{ cssPrefix }}"] {
44 | font-family: '{{ fontName }}';
45 | -webkit-font-smoothing: antialiased;
46 | -moz-osx-font-smoothing: grayscale;
47 | font-style: normal;
48 | }
49 |
50 | {% for glyph in glyphs %}
51 | .{{ cssPrefix }}-{{ glyph.name }}::before {
52 | content: "\{{ glyph.unicode[0].charCodeAt(0).toString(16) }}";
53 | }
54 | {% endfor %}
55 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // http://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parserOptions: {
6 | parser: 'babel-eslint',
7 | sourceType: 'module'
8 | },
9 | env: {
10 | browser: true,
11 | },
12 | extends: [
13 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
14 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
15 | 'plugin:vue/essential',
16 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md
17 | 'standard'
18 | ],
19 | // required to lint *.vue files
20 | plugins: [
21 | 'vue'
22 | ],
23 | // add your custom rules here
24 | rules: {
25 | //"off"或0 -关闭规则
26 | //"warn" 或1 - 开启规则, 使用警告 程序不会退出
27 | //"error"或2 - 开启规则, 使用错误 程序退出
28 |
29 | indent: ['error', 4],
30 | 'vue/script-indent': ['error', 4, { baseIndent: 1 }],
31 | //分号
32 | semi: 'off',
33 | 'spaced-comment': 'off',
34 | eqeqeq: 1,
35 | 'no-useless-escape': 'off',
36 | 'brace-style': 0, //大括号风格
37 | curly: 'off', //[2, "all"],//必须使用 if(){} 中的{}
38 | 'space-before-function-paren': ['off', 'always'], //函数定义时括号前面要不要有空格
39 | 'no-new': 'off',
40 | 'comma-dangle': 'off', //对象字面量项尾不能有逗号
41 | 'no-return-assign': 'warn', //return 语句中不能有赋值表达式
42 | 'eol-last': 0,
43 | 'no-multiple-empty-lines': 0,
44 | //'quotes': 'off',
45 | //'comma-spacing': 'off',
46 | 'handle-callback-err': 0,
47 | 'padded-blocks': 0,
48 | 'no-duplicate-imports': 0,
49 | 'operator-linebreak': 0,
50 | 'no-undef': 2,
51 | 'no-var': 1, //是否可使用var
52 | 'no-extend-native': 0,
53 | 'no-sequences': 0,
54 |
55 | // allow paren-less arrow functions
56 | 'arrow-parens': 0,
57 | // allow async-await
58 | 'generator-star-spacing': 0,
59 | // allow debugger during development
60 | 'no-debugger': 2,
61 | 'no-eval': 0,
62 | //'standard/no-callback-literal': 1,
63 | 'array-callback-return': 1,
64 | },
65 | overrides: [
66 | {
67 | files: ['*.vue'],
68 | rules: {
69 | indent: 'off'
70 | }
71 | }
72 | ]
73 | }
74 |
--------------------------------------------------------------------------------
/mock/root/api/getMenus.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | disabled: 0,
3 | status: 200,
4 | body: function (query, post) {
5 | return {
6 | status: 0,
7 | msg: '',
8 | data: [
9 | {
10 | title: 'Home',
11 | url: '/',
12 | icon: 'el-icon-home'
13 | },
14 | {
15 | title: 'Theme',
16 | url: '/theme',
17 | icon: 'el-icon-theme'
18 | },
19 | {
20 | title: 'Layout',
21 | url: '/layout',
22 | icon: 'el-icon-grid'
23 | },
24 | {
25 | title: 'Page1',
26 | url: '/stage1',
27 | children: [{
28 | title: 'stage1',
29 | url: '/stage1',
30 | icon: 'el-icon-document'
31 | }, {
32 | title: 'stage2',
33 | url: '/stage2',
34 | icon: 'el-icon-s-goods'
35 | }, {
36 | title: 'stage3',
37 | url: '/stage2/stage3',
38 | icon: 'el-icon-setting'
39 | }, {
40 | title: 'rest tool',
41 | url: '/zrest',
42 | icon: 'el-icon-menu'
43 | }]
44 | },
45 | {
46 | title: 'Page2',
47 | url: '/page2',
48 | children: [{
49 | title: 'Icons',
50 | url: '/icons',
51 | icon: 'el-icon-setting'
52 | }, {
53 | title: 'stage1',
54 | url: '/stage1',
55 | icon: 'el-icon-document'
56 | }, {
57 | title: 'stage2',
58 | url: '/stage2',
59 | icon: 'el-icon-goods'
60 | }, {
61 | title: 'rest tool',
62 | url: '/zrest',
63 | icon: 'el-icon-menu'
64 | }]
65 | }
66 | ]
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/views/iconfontPreview/iconfontPreview.vue:
--------------------------------------------------------------------------------
1 |
47 |
48 |
49 |
50 | Modify or add a svg file in dir [src/iconfont/svgs], they will be parsed to iconfonts with css, and hot-loaded.
51 | 修改或者添加svg文件到[src/iconfont/svgs]目录,将自动生成为iconfonts以及配套的css,支持热重载。
52 |
53 |
54 |
my-app-icon (5 icons, click to copy)
55 | Class name prefix: my-icon- You can use it like: <i class="my-icon-success"></i>
56 |
57 |
58 |
64 |
65 |
66 |
67 |
68 |
89 |
--------------------------------------------------------------------------------
/src/js/utils/domUtil.js:
--------------------------------------------------------------------------------
1 | /*
2 | dom操作类:主要用来绑定事件
3 | */
4 | function QueryEls(selector, context) {
5 | if (!selector)
6 | this.els = [];
7 | else if (typeof selector === 'string') //选择器
8 | this.els = (context || document).querySelectorAll(selector);
9 | else if (selector.addEventListener)
10 | this.els = [selector]; //单个dom元素
11 | else
12 | this.els = selector; //dom元素集合
13 | callEach(this.els, (el, i) => {
14 | this[i] = el
15 | }); //支持this[index]取dom节点
16 | }
17 |
18 | QueryEls.prototype = {
19 | //绑定事件
20 | on(event, handlerFn) {
21 | return this.each(el => el.addEventListener(event, handlerFn))
22 | },
23 | //解绑事件
24 | off(event, handlerFn) {
25 | return this.each(el => el.removeEventListener(event, handlerFn))
26 | },
27 | //从第一个子节点查找
28 | find(selector) {
29 | const el = this.els[0]; //注意,只取第一个
30 | return el ? new QueryEls(selector, el) : new QueryEls();
31 | },
32 | //遍历, fn(el, index)
33 | each(fn) {
34 | callEach(this.els, fn);
35 | return this;
36 | },
37 | elDo(doByEl, index = 0) { //针对第一个元素进行操作,或者返回值
38 | const e = this.els[index];
39 | return e && doByEl(e);
40 | },
41 | attr(name, val) {
42 | return getOrSet(this.els, e => e.getAttribute(name), e => e.setAttribute(name, val), arguments)
43 | },
44 | prop(name, val) {
45 | return getOrSet(this.els, e => e[name], e => e[name] = val, arguments)
46 | }
47 | }
48 |
49 | function getOrSet(els, getter, setter, args) {
50 | if (args.length === 1) {
51 | return els[0] && getter(els[0])
52 | } else {
53 | callEach(els, el => setter(el))
54 | }
55 | }
56 |
57 | function callEach(arr, fn) {
58 | for (let i = 0; i < arr.length; i++) {
59 | try {
60 | fn(arr[i], i);
61 | } catch (e) {
62 | }
63 | }
64 | }
65 |
66 | const $x = function (selector, context) {
67 | return new QueryEls(selector, context)
68 | };
69 |
70 | // $x.postTo = function postTo(url, data, target) {
71 | // var form = document.createElement('form');
72 | // form.style.display = 'none';
73 | // form.method = 'post';
74 | // form.action = url;
75 | // form.target = target || '';
76 | // for (var n in data) {
77 | // if (typeof (data[n]) != 'object' && typeof (data[n]) != 'function') {
78 | // var input = form.appendChild(document.createElement('input'))
79 | // input.name = n;
80 | // input.value = data[n]
81 | // }
82 | // }
83 | // document.body.appendChild(form).submit();
84 | // document.body.removeChild(form);
85 | // }
86 |
87 | export default $x;
88 |
--------------------------------------------------------------------------------
/src/css/flexible.scss:
--------------------------------------------------------------------------------
1 | /*移动端,响应式布局
2 | var rate = 0.6,std; // 375/(1/.16) /100 = 0.6
3 | var ret = [320, 360, 375, 400, 414, 440, 480, 520, 560, 600, 640, 680, 720, 750].map(i=>{
4 | var num = (i/0.6).toFixed(5).replace(/\.[0]+$/,'');
5 | if(i==375) std = num
6 | return ('@media (min-width:'+i+'px){html{font-size:'+ num +'% !important;}}')
7 | }
8 | ).join('\r\n') + '\r\n@media (min-width:768px){html{font-size:'+ std +'% !important;}}';
9 | copy(ret);
10 | console.log(ret);
11 | */
12 |
13 | /*768以上作为电脑屏,同iphone6的375。以rem为单位,每rem表示100像素*/
14 |
15 | //@media (max-width: 319px) {
16 | // html {
17 | // font-size: 533.33333% !important;
18 | // }
19 | //}
20 | //@media (min-width: 320px) {
21 | // html {
22 | // font-size: 533.33333% !important;
23 | // }
24 | //}
25 | //
26 | //@media (min-width: 360px) {
27 | // html {
28 | // font-size: 600% !important;
29 | // }
30 | //}
31 | //
32 | //@media (min-width: 375px) {
33 | // html {
34 | // font-size: 625% !important;
35 | // }
36 | //}
37 | //
38 | //@media (min-width: 400px) {
39 | // html {
40 | // font-size: 666.66667% !important;
41 | // }
42 | //}
43 | //
44 | //@media (min-width: 414px) {
45 | // html {
46 | // font-size: 690% !important;
47 | // }
48 | //}
49 | //
50 | //@media (min-width: 440px) {
51 | // html {
52 | // font-size: 733.33333% !important;
53 | // }
54 | //}
55 | //
56 | //@media (min-width: 480px) {
57 | // html {
58 | // font-size: 800% !important;
59 | // }
60 | //}
61 | //
62 | //@media (min-width: 520px) {
63 | // html {
64 | // font-size: 866.66667% !important;
65 | // }
66 | //}
67 | //
68 | //@media (min-width: 560px) {
69 | // html {
70 | // font-size: 933.33333% !important;
71 | // }
72 | //}
73 | //
74 | //@media (min-width: 600px) {
75 | // html {
76 | // font-size: 1000% !important;
77 | // }
78 | //}
79 | //
80 | //@media (min-width: 640px) {
81 | // html {
82 | // font-size: 1066.66667% !important;
83 | // }
84 | //}
85 | //
86 | //@media (min-width: 680px) {
87 | // html {
88 | // font-size: 1133.33333% !important;
89 | // }
90 | //}
91 | //
92 | //@media (min-width: 720px) {
93 | // html {
94 | // font-size: 1200% !important;
95 | // }
96 | //}
97 | //
98 | //@media (min-width: 750px) {
99 | // html {
100 | // font-size: 1250% !important;
101 | // }
102 | //}
103 |
104 | // 上述媒体查询用这一句css全搞定
105 | // 100vw / 375px = 0.2666667 vw/px
106 | // 为便于换算,设定1rem = 100px, 即 1rem = 26.66667vw
107 | html {
108 | font-size: 26.66667vw !important;
109 | }
110 |
111 | /*768以上作为PC端处理*/
112 | @media (min-width: 768px) {
113 | html {
114 | font-size: 625% !important;
115 | }
116 | }
117 |
118 | /*默认字体14px*/
119 | body {
120 | font-size: 14px;
121 | }
--------------------------------------------------------------------------------
/src/component/topHeader.vue:
--------------------------------------------------------------------------------
1 |
2 |
32 |
33 |
34 |
65 |
100 |
--------------------------------------------------------------------------------
/src/js/axios.js:
--------------------------------------------------------------------------------
1 | /*
2 | 自定义的axios 第3个参数(config)字段意义:
3 | {
4 | showError: 默认弹出toast框提示。showError='alert'表示弹出alert框。showError===false默认不弹出
5 | maskOptions: 表示请求时显示遮罩层的选项。默认{body: true}
6 | }
7 |
8 | */
9 |
10 | import Axios from 'axios'
11 | import appConfig from '../../config/app-config'
12 | import userInfo from './utils/userInfo'
13 | import msgDlg from './utils/msgDialog'
14 | import loading from './utils/loading'
15 |
16 | const serverMap = require('../../config/serverMap.js')
17 |
18 | const axios = Axios.create({
19 | baseURL: serverMap.base,
20 | withCredentials: true,
21 | timeout: 20000
22 | })
23 | axios.defaults.headers.post['Content-Type'] = 'application/json'
24 |
25 |
26 | axios.defaults.transformRequest = function (request) {
27 | return JSON.stringify(request)
28 | }
29 |
30 | const ShowMsg = '系统异常,请稍后重试~'
31 | // http请求拦截器
32 | axios.interceptors.request.use(function (config) {
33 | config.headers.token = userInfo.token
34 | //将 {node_api}/xxx/yyy 的url替换为对应服务的前缀
35 | config.url = config.url.replace(/^\{(\w+)\}/, (m, $1) => serverMap[$1] || '');
36 |
37 | //遮罩层
38 | loading.show(config.maskOptions)
39 |
40 | return config
41 | }, fail)
42 |
43 | // 0-成功,1-session超时,2-系统错误(提示ShowMsg),其他:提示错误
44 | const ResStatus = {
45 | OK: 0,
46 | SessionFail: 1,
47 | SysErr: 2,
48 | }
49 | // http响应拦截器
50 | axios.interceptors.response.use(function (res) {
51 | loading.close(res.config.maskOptions)
52 | const data = res.data || {};
53 | const status = Number(data.status);
54 | if (status === ResStatus.SessionFail) {
55 | if (axios.inLogin) return
56 | axios.inLogin = 1
57 | return new Promise((resolve, reject) => {
58 | msgDlg.confirm('登录已过期,请重新登录').then(() => {
59 | doLogin()
60 | reject(data)
61 | })
62 | });
63 | } else if (status !== ResStatus.OK) { //错误
64 | console.error(data)
65 | if (status === ResStatus.SysErr || !data.msg) {
66 | data.msg = ShowMsg
67 | }
68 | showErr(res.config, data.msg);
69 | return Promise.reject(data)
70 | }
71 | return data.data
72 | }, fail);
73 |
74 | function doLogin() {
75 | return new Promise((resolve, reject) => {
76 | msgDlg.alert('尚未登录或登录超时,请重新登录', '提示', {
77 | callback: action => {
78 | if (action === 'cancel')
79 | reject(new Error())
80 | else {
81 | let url = appConfig.LOGIN_PATH
82 | const path = location.href.match(/https?:\/\/[^\/]+(\/.+)/i)[1]
83 | if (path && path !== '/main.html#/') {
84 | url += url.indexOf('?') > -1 ? '&' : '?'
85 | url += 'redirectUrl=' + encodeURIComponent(path)
86 | }
87 | location.href = url
88 | resolve()
89 | }
90 | }
91 | })
92 | });
93 | }
94 |
95 | function fail(error) {
96 | if (error.config) loading.close(error.config.maskOptions);
97 | if (!error.msg) error.msg = ShowMsg;
98 | showErr(error.config, ShowMsg);
99 | console.error(error)
100 | return Promise.reject(error)
101 | }
102 |
103 | function showErr(config, errmsg) {
104 | if (errmsg) {
105 | if (config && config.showError === 'alert')
106 | msgDlg.alert(errmsg, { type: 'error' });
107 | else
108 | msgDlg.toast.error(errmsg);
109 | }
110 | }
111 |
112 | export default axios
113 |
--------------------------------------------------------------------------------
/src/iconfont/fonts/_font-preview.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IconFonts Preview Demo -- my-app-icon
6 |
7 |
8 |
9 |
61 |
69 |
70 |
71 | Use textbox like password (prevent to save password):
72 |
73 | Modify or add a svg file in dir [src/iconfont/svgs], they will be parsed to iconfonts with css, and hot-loaded.
74 | 修改或者添加svg文件到[src/iconfont/svgs]目录,将自动生成为iconfonts以及配套的css,支持热重载。
75 |
76 |
77 |
my-app-icon (6 icons, click to copy)
78 | Class name prefix: my-icon- You can use it like: <i class="my-icon-success"></i>
79 |
80 |
81 |
82 |
83 |
87 |
88 |
92 |
93 |
97 |
98 |
102 |
103 |
107 |
108 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-element-ui-scaffold-webpack4
2 |
3 | # 注:
4 | * **目前已升级为webpack5**,如需要使用webpack4,可切换分支:`git checkout webpack4`。
5 |
6 | * **vue3 + element-ui + webpack5 版本:可切换分支:`git checkout vue3` **
7 |
8 | 本项目为vue下使用了element-plus并基于webpack5构建的多页面、多环境方案脚手架项目。
9 |
10 | [项目效果预览1](http://test.hz300.com/webpack4/)
11 | [项目效果预览2](https://hzsrc-vue-webpack4-elementui.netlify.com/)
12 |
13 |
14 | ## 1. 安装
15 | ```
16 | git clone https://github.com/hzsrc/vue-element-ui-scaffold-webpack4.git
17 | # git checkout vue3
18 | cd vue-element-ui-scaffold-webpack4
19 | npm install
20 | ```
21 |
22 | ## 2. 特性
23 | ### 基于webpack5 + babel@7 构建
24 | 更快的构建速度,更小的打包文件体积。
25 |
26 | ### 多页面实现
27 | 两种方式,自动输出html页面文件(html-webpack-plugin实现):
28 | * 在src/pages目录下添加任意js文件。js文件作为webpack入口;html页面模板是public/index.html,页面的文件名为js的文件名。
29 | * 在src/pages目录下建立任意文件夹,包含entry.js、template.html两个文件。entry.js作为webpack入口;html页面模板是template.html,页面的文件名为建立的文件夹名。
30 |
31 | ### 自动用svg生成iconfont字体图标,支持webpack热重载
32 | 开发时在src/iconfont/svgs目录下,修改或添加、删除svg文件,可自动生成字体图标(支持ttf,woff2,woff,eot,svg)及配套的css样式、html预览;同时热重载立即可以看到效果。
33 | 也可npm run build-font手动生成这些文件。
34 | 无需再手动去icomoon.io或iconfont.cn生成和修改字体图标、css、图标预览了。
35 | 基于[webpack-iconfont-plugin-nodejs](https://github.com/hzsrc/webpack-iconfont-plugin-nodejs)实现。
36 |
37 | ### mock数据实现
38 | 项目可采用[dynamic-mocker](https://github.com/hzsrc/dynamic-mocker)作为后端接口的数据模拟。
39 | 模拟数据位于mock文件夹下,采用js文件实现,易于理解且方便灵活。
40 |
41 | 启用方法:
42 | 1、npm run dev默认会同时启动mock服务。
43 | 2、单独运行:npm run mock。
44 |
45 | 配置文件:
46 | 1、config/serverMap.js中的接口服务地址为:base: '"//localhost:8085"'
47 | 2、mock/mock-config.js文件配置mock各种参数。
48 |
49 | ### element-plus按需加载,主题色全局切换
50 | css按需加载的来源直接指向element-plus的scss文件,而不是预编译的css文件。通过join-file-content-plugin插件在编译时将src/assets/css/element-theme/theme-changed.scss文件 附加到element-plus主题变量文件theme-chalk/src/common/var.scss之前,实现了在修改scss变量后即可立马查看效果,无需预先编译element-plus的scss文件为css文件。同时可以在项目任意地方引用element-plus的scss变量。
51 |
52 | ### 运行时动态调整主题色(含自写的主题样式)
53 | 利用[webpack-theme-color-replacer](https://github.com/hzsrc/webpack-theme-color-replacer)插件,在webpack构建时提取css中含有主题色的样式规则,生成一个css/theme-colors.css文件。然后在网页运行时,下载这个css文件,动态替换其中的颜色为自定义主题色。由于只提取了颜色相关的css,故速度比下载element-plus整个css要快很多。而且不仅仅是element-plus的样式,项目中的自写样式的主题色也可以一并替换掉。
54 |
55 | ### 源码映射
56 | 发布代码时生成源码映射文件到统一的源码映射文件夹,并在测试环境自动映射。生产环境为了代码安全,不进行自动映射,如需调试支持chrome通过url手动映射源码。
57 | 根据安全要求,这个源码映射文件夹名是只有开发者知道的文件夹名。或采是用动态加密算法生成此文件夹名。或者将这些源码映射文件放到需要进行登录验证的网站目录下。目录的名称请根据需要自行在`config/index.js`文件的`getSourceMapPath`函数中修改。
58 | 这样既可在出现bug需要进行线上调试时,快速手动添加源码映射来方便调试,又避免了源码泄露。
59 |
60 | ### 响应式布局
61 | 采用vw+rem的简洁方案实现响应式布局。
62 | 使用postcss-pxtorem插件自动将css中的单位由px转化为rem,开发时仍使用px做为css长度单位。1rem = 100px,调试时换算方便。pc和移动端通用(移动端最好将element-plus换为其他UI框架)。
63 |
64 | ### 浏览器兼容性
65 | 兼容IE10及以上、Chrome、Firefox、Safari、QQ、360、2345等浏览器。如果需要改为移动端,请修改.browsersrc为移动端版本。
66 |
67 | ## 3. 命令说明
68 | ### 本地开发
69 | ```
70 | npm run dev
71 | ```
72 | 本地开发调试。使用config/serverMap.js中的dev配置的后端接口服务地址。
73 |
74 | ### 发布测试环境
75 | ```
76 | npm run build-test
77 | ```
78 | 用于测试环境部署。js带源码映射,css无源码映射。使用config/serverMap.js中的test配置的接口服务地址。
79 |
80 | ### 发布生产环境
81 | ```
82 | npm run build
83 | ```
84 | 用于生产环境部署。使用config/serverMap.js中的prod配置的接口服务地址。
85 |
86 | ### 发布演示环境
87 | ```
88 | npm run build-demo
89 | ```
90 | 配置同生产环境,仅接口服务地址不同,使用config/serverMap.js中的demo配置的接口服务地址。
91 |
92 | ### 发布开发环境
93 | ```
94 | npm run build-dev
95 | ```
96 | 用于发布部署到开发环境服务器,适用于需要发布到服务器才能调试的情形。使用config/serverMap.js中的dev配置的接口服务地址。
97 |
98 | ### 启用mock数据发布
99 | ```
100 | npm run build-preview
101 | ```
102 | 会启用静态mock数据,无需后端服务,使用mock数据来模拟ajax调用(前提是对应的api接口写了mock数据)。
103 | 等同于`npm run build --preview && npm run play-dist`。
104 |
105 | ### 查看dist目录运行结果
106 | ```
107 | npm run play-dist
108 | ```
109 | 以dist目录为根目录,启动一个本地静态http服务,用于查看发布后dist目录的运行结果。
110 |
111 | ### 启动mock服务
112 | ```
113 | npm run mock
114 | ```
115 | 当后端接口服务尚未完成时,可用于模拟后端接口数据调试前端功能。
116 |
117 |
118 | ### 代理到80端口或443端口
119 | ```
120 | npm run proxy80
121 | ```
122 | 通过将现有端口(80xx端口)代理到80端口或443端口,可实现访问时隐藏端口,也可实现https访问。结合系统hosts配置127.0.0.1为指定的域名,可直接用域名访问本地调试页面,用于调试一些必须使用域名访问的场景(例如调试微信,详见:https://www.cnblogs.com/hz-blog/p/wechat-local-debug-domain.html)。
123 |
124 |
125 |
--------------------------------------------------------------------------------
/src/views/home/homeInner.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Home Page
4 |
5 |
6 | call mocked api
7 | call base api
8 | call base api(not mocked)
9 | call other api servers by {xxx} prefix
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | {{$x.formatDate(row.date)}}
18 |
19 |
20 |
21 | Edit
22 |
23 |
24 |
25 |
27 |
28 |
29 |
stage1
30 |
stage2
31 |
stage3
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
104 |
105 |
110 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-element-ui-scaffold-webpack4",
3 | "version": "1.0.9",
4 | "description": "vue-element-ui-scaffold-webpack4",
5 | "author": "huangzheng",
6 | "private": true,
7 | "scripts": {
8 | "dev": "cross-env NODE_ENV=development ENV_CONFIG=dev node build/dev-server.js --mock",
9 | "start": "npm run dev",
10 | "build-dev": "cross-env NODE_ENV=production ENV_CONFIG=dev_build node build/build.js",
11 | "build-test": "cross-env NODE_ENV=production ENV_CONFIG=test node build/build.js",
12 | "build-demo": "cross-env NODE_ENV=production ENV_CONFIG=demo node build/build.js",
13 | "build": "cross-env NODE_ENV=production ENV_CONFIG=prod node build/build.js",
14 | "build-preview": "npm run build --preview && npm run play-dist",
15 | "play-dist": "node -e \"require('dynamic-mocker').start('./mock/static-config.js')\"",
16 | "mock": "node -e \"require('dynamic-mocker').start('./mock/mock-config.js')\" ",
17 | "proxy80": "node -e \"require('dynamic-mocker').start('./mock/mock-proxy80.js')\" ",
18 | "lint": "eslint --ext .js,.vue src",
19 | "fixlint": "eslint --fix --ext .js,.vue src mock",
20 | "build-font": "node -e \"require('./build/svg2font.js').build()\"",
21 | "show-map": "node -e \"console.log(require('./config').build.sourceMapPath)\"",
22 | "rmcache": "node -e \"require('rimraf')('./node_modules/.cache', console.log)\"",
23 | "set-names": "node -e \"require('vue-auto-name')('src')\""
24 | },
25 | "dependencies": {
26 | "axios": "^1.7.5",
27 | "date-any": "^1.0.7",
28 | "echarts": "^5.3.0",
29 | "element-ui": "^2.15.14",
30 | "http-vue-loader-mime": "^1.4.3",
31 | "lodash": "*",
32 | "qrcode-vue": "^1.2.0",
33 | "slot-layout": "^1.0.5",
34 | "vue": "^2.6.12",
35 | "vue-router": "^3.5.4",
36 | "vue-smooth-dnd": "^0.8.1",
37 | "vuex": "^3.6.2"
38 | },
39 | "devDependencies": {
40 |
41 | "@babel/core": "^7.23.3",
42 | "@babel/plugin-proposal-class-properties": "^7.18.6",
43 | "@babel/preset-env": "^7.23.3",
44 | "@babel/preset-stage-2": "^7.8.3",
45 | "@babel/register": "^7.22.15",
46 | "@vue/babel-preset-jsx": "^1.4.0",
47 | "autoprefixer": "^10.2.5",
48 | "babel-eslint": "^10.1.0",
49 | "babel-loader": "^8.3.0",
50 | "babel-plugin-component": "^1.1.1",
51 | "bluebird": "^3.7.2",
52 | "chalk": "^4.1.0",
53 | "copy-dir": "^1.3.0",
54 | "copy-webpack-plugin": "^8.1.1",
55 | "core-js": "^3.33.3",
56 | "cross-env": "^7.0.3",
57 | "cross-spawn": "^7.0.3",
58 | "css-loader": "^5.2.7",
59 | "css-minimizer-webpack-plugin": "^3.4.1",
60 | "dynamic-mocker": "^1.2.19",
61 | "eslint": "^7.25.0",
62 | "eslint-config-standard": "^16.0.2",
63 | "eslint-friendly-formatter": "4.0.1",
64 | "eslint-plugin-import": "^2.28.1",
65 | "eslint-plugin-node": "11.1.0",
66 | "eslint-plugin-promise": "^5.1.0",
67 | "eslint-plugin-standard": "4.0.2",
68 | "eslint-plugin-vue": "^7.9.0",
69 | "eventsource-polyfill": "^0.9.6",
70 | "express": "^4.19.2",
71 | "file-loader": "^6.2.0",
72 | "glob": "<=7.*",
73 | "globby": "^11.0.3",
74 | "html-webpack-plugin": "^5.5.0",
75 | "join-file-content-plugin": "latest",
76 | "js-conditional-compile-loader": "^1.0.16",
77 | "js-file-tool": "^1.0.8",
78 | "mini-css-extract-plugin": "^2.7.6",
79 | "opn": "^6.0.0",
80 | "ora": "^5.4.0",
81 | "postcss-loader": "6.2.1",
82 | "postcss-pxtorem": "6.1.0",
83 | "rimraf": "^3.0.2",
84 | "sass": "=1.32.13",
85 | "sass-loader": "^13.3.2",
86 | "shelljs": "^0.8.5",
87 | "text-encode": "^1.0.3",
88 | "uglify-js": "^3.13.5",
89 | "url-loader": "^4.1.1",
90 | "vue-auto-name": "^1.0.5",
91 | "vue-loader": "^15.11.1",
92 | "vue-style-loader": "^4.1.3",
93 | "vue-template-compiler": "^2.6.12",
94 | "webpack": "^5.94.0",
95 | "webpack-bundle-analyzer": "^4.10.1",
96 | "webpack-dev-middleware": "^5.3.4",
97 | "webpack-hot-middleware": "^2.25.4",
98 | "webpack-iconfont-plugin-nodejs": "^1.0.36",
99 | "webpack-merge": "^5.10.0",
100 | "webpack-theme-color-replacer": "^1.5.2"
101 | },
102 | "engines": {
103 | "node": ">= 4.0.0",
104 | "npm": ">= 3.0.0"
105 | },
106 | "directories": {
107 | "test": "test"
108 | },
109 | "repository": {
110 | "type": "git"
111 | },
112 | "keywords": []
113 | }
114 |
--------------------------------------------------------------------------------
/src/css/utils.scss:
--------------------------------------------------------------------------------
1 | html {
2 | height: 100%
3 | }
4 |
5 | body {
6 | height: 100%;
7 | margin: 0;
8 | padding: 0;
9 | }
10 |
11 | ul {
12 | padding-left: 0;
13 | margin: 0
14 | }
15 |
16 | li {
17 | list-style: none;
18 | }
19 |
20 | h1, h2, h3, h4, h5, h6 {
21 | margin: 0;
22 | font-weight: normal;
23 | }
24 |
25 | a {
26 | cursor: pointer;
27 | text-decoration: none;
28 | }
29 |
30 | div, section, nav, p, article, header, footer, h1, h2, h3, h4, h5, h6, ul, li, ol {
31 | box-sizing: border-box;
32 | }
33 |
34 | body, input, select, textarea, button {
35 | font-family: 'Microsoft YaHei', "PingFang SC", "Hiragino Sans GB", -apple-system, Arial;
36 | }
37 |
38 | .full {
39 | height: 100%;
40 | width: 100%;
41 | }
42 |
43 | /* 自适应布局*/
44 | .full-ctn {
45 | position: relative;
46 | width: 100%;
47 | height: 100%;
48 | box-sizing: border-box;
49 | }
50 |
51 | .full-ctn > * {
52 | position: absolute;
53 | height: 100%;
54 | width: 100%;
55 | box-sizing: border-box;
56 | }
57 |
58 | .lined {
59 | border-bottom: 1px solid #eee;
60 | }
61 |
62 | .toplined {
63 | border-top: 1px solid #eee;
64 | }
65 |
66 | .shaddowed {
67 | box-shadow: 0 0 4px 1px rgba(#000, 0.1);
68 | }
69 |
70 | .pre {
71 | white-space: pre-wrap;
72 | line-height: 1.5;
73 | word-break: break-all;
74 | }
75 |
76 | .hide {
77 | display: none;
78 | }
79 |
80 | .hide_v {
81 | visibility: hidden;
82 | }
83 |
84 | .inlnb {
85 | display: inline-block;
86 | }
87 |
88 | .no-border {
89 | border: none;
90 | }
91 |
92 | .bordered {
93 | border: 1px solid #ddd;
94 | }
95 |
96 | .radius {
97 | border-radius: 5px;
98 | }
99 |
100 | .pointer, .pointer * {
101 | cursor: pointer;
102 | }
103 |
104 | .lay-tbl {
105 | display: table;
106 | }
107 |
108 | .lay-cell {
109 | display: table-cell;
110 | vertical-align: middle;
111 | }
112 |
113 | .left {
114 | float: left;
115 | }
116 |
117 | .right {
118 | float: right;
119 | }
120 |
121 | .clearfix {
122 | clear: both;
123 | }
124 |
125 | .clearfix-child:after {
126 | content: '';
127 | clear: both;
128 | display: block;
129 | }
130 |
131 | .b-center {
132 | margin: auto;
133 | }
134 |
135 | .t-left {
136 | text-align: left;
137 | }
138 |
139 | .t-center {
140 | text-align: center;
141 | }
142 |
143 | .flex {
144 | display: flex;
145 | }
146 |
147 | .f-middle {
148 | display: flex;
149 | align-items: center;
150 | }
151 |
152 | .f-center {
153 | display: flex;
154 | justify-content: center;
155 | }
156 |
157 | .f-wrap {
158 | flex-wrap: wrap;
159 | }
160 |
161 | .f-center {
162 | display: flex;
163 | align-items: center;
164 | justify-content: center;
165 | }
166 |
167 | .f-column {
168 | display: flex;
169 | flex-direction: column;
170 | }
171 |
172 | .no-shrink {
173 | flex-shrink: 0;
174 | }
175 |
176 | .f-grow {
177 | flex-grow: 1;
178 | }
179 |
180 | .f-end {
181 | justify-content: flex-end;
182 | }
183 |
184 | .t-right {
185 | text-align: right;
186 | }
187 |
188 | .v-top {
189 | vertical-align: top;
190 | }
191 |
192 | .v-middle {
193 | vertical-align: middle;
194 | }
195 |
196 | .v-bottom {
197 | vertical-align: bottom;
198 | }
199 |
200 | .w100 {
201 | width: 100%;
202 | }
203 |
204 | .h100 {
205 | height: 100%;
206 | }
207 |
208 | table {
209 | border-collapse: collapse;
210 | border-spacing: 0;
211 | }
212 |
213 | .clear:after {
214 | content: ".";
215 | display: block;
216 | height: 0;
217 | clear: both;
218 | visibility: hidden
219 | }
220 |
221 | .ellipsis {
222 | white-space: nowrap;
223 | overflow: hidden;
224 | text-overflow: ellipsis;
225 | }
226 |
227 | .nowrap {
228 | white-space: nowrap;
229 | }
230 |
231 | .auto-bar {
232 | overflow: auto;
233 | }
234 |
235 | .absolute {
236 | position: absolute;
237 | }
238 |
239 | .relative {
240 | position: relative;
241 | }
242 |
243 | .white-bg {
244 | background: #fff;
245 | }
246 |
247 | .pre {
248 | white-space: pre-wrap;
249 | }
250 |
251 | .bold {
252 | font-weight: bold;
253 | }
254 |
255 | //最大4块,自动调整为4、3、2、1块并撑满
256 | .flex-4 {
257 | flex-basis: 21%;
258 | flex-grow: 1;
259 | }
260 |
261 | @media (max-width: 1400px) {
262 | .flex-4 {
263 | flex-basis: 26%;
264 | }
265 | }
266 |
267 | @media (max-width: 1100px) {
268 | .flex-4 {
269 | flex-basis: 34%;
270 | }
271 | }
272 |
273 | @media (max-width: 800px) {
274 | .flex-4 {
275 | flex-basis: 51%;
276 | }
277 | }
278 |
279 |
280 | //常用的10px 20px间距和空白
281 | $areas: (t: top, r: right, b: bottom, l: left);
282 | $sizes: 20 10;
283 | @each $size in $sizes {
284 | .pd-#{$size} {
285 | padding: #{$size}px;
286 | }
287 | .mg-#{$size} {
288 | margin: #{$size}px;
289 | }
290 | }
291 |
292 | @each $size in $sizes {
293 | @each $key, $area in $areas {
294 | .pd#{$key}-#{$size} {
295 | padding-#{$area}: #{$size}px;
296 | }
297 | .mg#{$key}-#{$size} {
298 | margin-#{$area}: #{$size}px;
299 | }
300 | }
301 | }
302 |
303 | //0间距
304 | @each $key, $area in $areas {
305 | .pd#{$key}-0 {
306 | padding-#{$area}: 0;
307 | }
308 | }
309 |
--------------------------------------------------------------------------------
/src/iconfont/svgs/state-beinvited.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | state-beinvited
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/views/themeColor/themeColor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Change theme color to:
5 |
6 | Reset
7 |
8 |
9 |
10 |
11 | element-ui css:
12 |
13 |
default
14 |
primary
15 |
16 |
17 |
18 |
19 |
text
20 | |
21 |
default
22 |
primary
23 |
24 |
25 |
26 |
27 |
28 |
default
29 |
primary
30 |
31 |
32 |
33 |
34 |
text
35 | |
36 |
default
37 |
primary
38 |
39 |
40 |
41 |
42 |
43 |
44 |
Color of element-ui css
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
56 |
57 |
59 |
60 |
61 |
66 |
67 |
68 |
69 |
70 |
self css:
71 |
72 | primary color in self css
73 | primary color series in self css
74 | primary color series in self css
75 |
76 |
77 |
other custom colors:
78 |
79 | my custom color
80 |
81 |
82 | my custom color 2
83 |
84 |
85 |
86 |
<< Back
87 |
88 |
89 |
90 |
91 |
92 |
93 |
126 |
127 |
174 |
--------------------------------------------------------------------------------
/src/iconfont/fonts/fonts.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default [
3 | {
4 | "name": "bookmark",
5 | "unicode": "12c",
6 | "svg": "\n\n \n Path 2 \n Created with Sketch. \n \n \n \n \n \n \n \n"
7 | },
8 | {
9 | "name": "calendar",
10 | "unicode": "12d",
11 | "svg": "\n \n "
12 | },
13 | {
14 | "name": "state-beinvited",
15 | "unicode": "12e",
16 | "svg": "\n\nstate-beinvited \n \n \n"
17 | },
18 | {
19 | "name": "success",
20 | "unicode": "12f",
21 | "svg": "\n\nsuccess \n \n \n"
22 | },
23 | {
24 | "name": "warning",
25 | "unicode": "130",
26 | "svg": "\n\nwarning1 \n \n \n"
27 | },
28 | {
29 | "name": "pwd_msk",
30 | "unicode": "7f",
31 | "svg": " "
32 | }
33 | ]
34 |
--------------------------------------------------------------------------------
/src/views/zrest/zrest.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 登录
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | 加载RAP接口列表
18 |
19 |
20 |
21 |
22 |
23 |
24 | 导出当前接口配置
25 | 查看RAP
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | 加载接口配置
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | {{request.headers}}
73 |
74 |
75 | 发起请求
76 |
77 |
78 |
79 |
HTTP响应
80 |
81 |
82 | {{response.result}} {{response.status}}
83 |
84 |
85 |
86 | {{response.json}}
87 |
88 |
89 |
90 |
91 | 接口调试工具
92 |
2018.3. By HuangZheng
93 |
94 |
95 |
96 |
97 |
307 |
308 |
319 |
--------------------------------------------------------------------------------
/src/iconfont/fonts/my-app-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
13 |
16 |
19 |
22 |
25 |
28 |
31 |
34 |
37 |
40 |
43 |
46 |
49 |
52 |
55 |
58 |
61 |
64 |
67 |
70 |
73 |
76 |
79 |
82 |
85 |
88 |
91 |
94 |
97 |
100 |
103 |
106 |
109 |
112 |
115 |
118 |
121 |
124 |
127 |
130 |
133 |
136 |
139 |
142 |
145 |
148 |
151 |
154 |
157 |
160 |
163 |
166 |
169 |
172 |
175 |
178 |
181 |
184 |
187 |
190 |
193 |
196 |
199 |
202 |
205 |
208 |
211 |
214 |
217 |
220 |
223 |
226 |
229 |
232 |
235 |
238 |
241 |
244 |
247 |
250 |
253 |
256 |
259 |
262 |
265 |
268 |
271 |
274 |
277 |
280 |
283 |
286 |
289 |
292 |
295 |
298 |
301 |
304 |
307 |
310 |
313 |
314 |
315 |
316 |
--------------------------------------------------------------------------------