@@ -1,5 +1,14 @@ | |||||
[*.{js,jsx,ts,tsx,vue}] | |||||
# http://editorconfig.org | |||||
root = true | |||||
[*] | |||||
charset = utf-8 | |||||
indent_style = space | indent_style = space | ||||
indent_size = 2 | indent_size = 2 | ||||
trim_trailing_whitespace = true | |||||
end_of_line = lf | |||||
insert_final_newline = true | insert_final_newline = true | ||||
trim_trailing_whitespace = true | |||||
[*.md] | |||||
insert_final_newline = false | |||||
trim_trailing_whitespace = false |
@@ -0,0 +1,4 @@ | |||||
NODE_ENV='development' | |||||
# must start with VUE_APP_ | |||||
VUE_APP_ENV = 'development' | |||||
@@ -0,0 +1,4 @@ | |||||
NODE_ENV='production' | |||||
# must start with VUE_APP_ | |||||
VUE_APP_ENV = 'production' | |||||
@@ -0,0 +1,4 @@ | |||||
NODE_ENV='production' | |||||
# must start with VUE_APP_ | |||||
VUE_APP_ENV = 'staging' | |||||
@@ -3,15 +3,15 @@ module.exports = { | |||||
env: { | env: { | ||||
node: true | node: true | ||||
}, | }, | ||||
extends: [ | |||||
'plugin:vue/essential', | |||||
'@vue/standard' | |||||
], | |||||
extends: ['plugin:vue/essential', 'eslint:recommended', 'plugin:prettier/recommended'], | |||||
parserOptions: { | parserOptions: { | ||||
parser: '@babel/eslint-parser' | parser: '@babel/eslint-parser' | ||||
}, | }, | ||||
rules: { | rules: { | ||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', | ||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' | |||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', | |||||
'vue/multi-word-component-names': 'off', // 关闭名称校验, | |||||
semi: [0], //分号检验,默认不检验 | |||||
'prettier/prettier': 'off' | |||||
} | } | ||||
} | |||||
}; |
@@ -0,0 +1,19 @@ | |||||
// https://github.com/michael-ciniawsky/postcss-load-config | |||||
module.exports = ({ file }) => { | |||||
return { | |||||
plugins: { | |||||
autoprefixer: {}, | |||||
'postcss-px-to-viewport': { | |||||
unitToConvert: 'px', // 要转化的单位 | |||||
viewportWidth: file.includes('vant') ? 375 : 750, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750 | |||||
viewportHeight: 812, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置 | |||||
unitPrecision: 6, // 指定`px`转换为视窗单位值的小数位数 | |||||
viewportUnit: 'vw', //指定需要转换成的视窗单位,建议使用vw | |||||
selectorBlackList: [], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名 | |||||
minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值 | |||||
mediaQuery: false // 允许在媒体查询中转换`px` | |||||
} | |||||
} | |||||
}; | |||||
}; |
@@ -0,0 +1,24 @@ | |||||
{ | |||||
"printWidth": 120, | |||||
"tabWidth": 2, | |||||
"singleQuote": true, | |||||
"trailingComma": "none", | |||||
"semi": true, | |||||
"wrap_line_length": 120, | |||||
"wrap_attributes": "auto", | |||||
"proseWrap": "always", | |||||
"arrowParens": "avoid", | |||||
"bracketSpacing": true, | |||||
"jsxBracketSameLine": true, | |||||
"useTabs": false, | |||||
"eslintIntegration":true, | |||||
"overrides": [ | |||||
{ | |||||
"files": ".prettierrc", | |||||
"options": { | |||||
"parser": "json" | |||||
} | |||||
} | |||||
], | |||||
"endOfLine": "auto" | |||||
} |
@@ -1,5 +1,23 @@ | |||||
module.exports = { | |||||
presets: [ | |||||
'@vue/cli-plugin-babel/preset' | |||||
// 获取 VUE_APP_ENV 非 NODE_ENV,测试环境依然 console | |||||
const IS_PROD = ['production', 'prod'].includes(process.env.VUE_APP_ENV); | |||||
const plugins = [ | |||||
[ | |||||
'import', | |||||
{ | |||||
libraryName: 'vant', | |||||
libraryDirectory: 'es', | |||||
style: true | |||||
}, | |||||
'vant' | |||||
] | ] | ||||
]; | |||||
// 去除 console.log | |||||
if (IS_PROD) { | |||||
plugins.push('transform-remove-console'); | |||||
} | } | ||||
module.exports = { | |||||
presets: [['@vue/cli-plugin-babel/preset', { useBuiltIns: 'usage', corejs: 3 }]], | |||||
plugins | |||||
}; |
@@ -1,3 +1,3 @@ | |||||
module.exports = { | module.exports = { | ||||
'*.{js,jsx,vue}': 'vue-cli-service lint' | '*.{js,jsx,vue}': 'vue-cli-service lint' | ||||
} | |||||
}; |
@@ -1,38 +1,46 @@ | |||||
{ | { | ||||
"name": "health_students_web", | |||||
"version": "0.1.0", | |||||
"name": "vue-h5-template", | |||||
"version": "2.1.0", | |||||
"description": "A vue h5 template with Vant UI", | |||||
"author": "Sunnie <sunniejs@163.com>", | |||||
"private": true, | "private": true, | ||||
"scripts": { | "scripts": { | ||||
"serve": "vue-cli-service serve", | "serve": "vue-cli-service serve", | ||||
"build": "vue-cli-service build", | "build": "vue-cli-service build", | ||||
"lint": "vue-cli-service lint" | |||||
"stage": "vue-cli-service build --mode staging", | |||||
"lint": "vue-cli-service lint", | |||||
"deps": "yarn upgrade-interactive --latest" | |||||
}, | }, | ||||
"dependencies": { | "dependencies": { | ||||
"core-js": "^3.8.3", | |||||
"vue": "^2.6.14", | |||||
"vue-router": "^3.5.1", | |||||
"amfe-flexible": "^2.2.1", | |||||
"axios": "^1.3.4", | |||||
"core-js": "^3.23.3", | |||||
"regenerator-runtime": "^0.13.5", | |||||
"vant": "^2.12.48", | |||||
"vue": "^2.7.8", | |||||
"vue-router": "^3.5.4", | |||||
"vuex": "^3.6.2" | "vuex": "^3.6.2" | ||||
}, | }, | ||||
"devDependencies": { | "devDependencies": { | ||||
"@babel/core": "^7.12.16", | |||||
"@babel/eslint-parser": "^7.12.16", | |||||
"@vue/cli-plugin-babel": "~5.0.0", | |||||
"@vue/cli-plugin-eslint": "~5.0.0", | |||||
"@vue/cli-plugin-router": "~5.0.0", | |||||
"@vue/cli-plugin-vuex": "~5.0.0", | |||||
"@vue/cli-service": "~5.0.0", | |||||
"@vue/eslint-config-standard": "^6.1.0", | |||||
"eslint": "^7.32.0", | |||||
"eslint-plugin-import": "^2.25.3", | |||||
"eslint-plugin-node": "^11.1.0", | |||||
"eslint-plugin-promise": "^5.1.0", | |||||
"eslint-plugin-vue": "^8.0.3", | |||||
"lint-staged": "^11.1.2", | |||||
"sass": "^1.32.7", | |||||
"sass-loader": "^12.0.0", | |||||
"vue-template-compiler": "^2.6.14" | |||||
}, | |||||
"gitHooks": { | |||||
"pre-commit": "lint-staged" | |||||
"@babel/core": "^7.18.10", | |||||
"@babel/eslint-parser": "^7.18.2", | |||||
"@vue/cli-plugin-babel": "~5.0.8", | |||||
"@vue/cli-plugin-eslint": "~5.0.8", | |||||
"@vue/cli-plugin-router": "~5.0.8", | |||||
"@vue/cli-plugin-vuex": "~5.0.8", | |||||
"@vue/cli-service": "~5.0.8", | |||||
"babel-eslint": "^10.1.0", | |||||
"babel-plugin-import": "^1.13.5", | |||||
"babel-plugin-transform-remove-console": "^6.9.4", | |||||
"eslint": "^8.22.0", | |||||
"eslint-config-prettier": "^8.5.0", | |||||
"eslint-plugin-prettier": "^4.2.1", | |||||
"eslint-plugin-vue": "^9.2.0", | |||||
"postcss-px-to-viewport": "^1.1.1", | |||||
"prettier": "^2.7.1", | |||||
"sass": "^1.54.4", | |||||
"sass-loader": "^13.0.2", | |||||
"script-ext-html-webpack-plugin": "^2.1.4", | |||||
"webpack-bundle-analyzer": "^4.5.0" | |||||
} | } | ||||
} | } |
@@ -1,17 +1,27 @@ | |||||
<!DOCTYPE html> | <!DOCTYPE html> | ||||
<html lang=""> | |||||
<html> | |||||
<head> | <head> | ||||
<meta charset="utf-8"> | <meta charset="utf-8"> | ||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||||
<meta name="viewport" content="width=device-width,initial-scale=1.0"> | |||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> | |||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> | |||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> | <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | ||||
<title><%= htmlWebpackPlugin.options.title %></title> | |||||
<!-- <% for (var i in | |||||
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %> | |||||
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" /> | |||||
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" /> | |||||
<% } %> --> | |||||
<title><%= webpackConfig.name %></title> | |||||
</head> | </head> | ||||
<body> | <body> | ||||
<noscript> | <noscript> | ||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> | |||||
<strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> | |||||
</noscript> | </noscript> | ||||
<div id="app"></div> | <div id="app"></div> | ||||
<!-- 使用CDN加速的JS文件,配置在vue.config.js下 --> | |||||
<!-- <% for (var i in | |||||
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %> | |||||
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script> | |||||
<% } %> --> | |||||
<!-- built files will be auto injected --> | <!-- built files will be auto injected --> | ||||
</body> | </body> | ||||
</html> | </html> |
@@ -1,32 +1,11 @@ | |||||
<template> | <template> | ||||
<div id="app"> | <div id="app"> | ||||
<nav> | |||||
<router-link to="/">Home</router-link> | | |||||
<router-link to="/about">About</router-link> | |||||
</nav> | |||||
<router-view/> | |||||
<router-view /> | |||||
</div> | </div> | ||||
</template> | </template> | ||||
<style lang="scss"> | |||||
#app { | |||||
font-family: Avenir, Helvetica, Arial, sans-serif; | |||||
-webkit-font-smoothing: antialiased; | |||||
-moz-osx-font-smoothing: grayscale; | |||||
text-align: center; | |||||
color: #2c3e50; | |||||
} | |||||
nav { | |||||
padding: 30px; | |||||
a { | |||||
font-weight: bold; | |||||
color: #2c3e50; | |||||
&.router-link-exact-active { | |||||
color: #42b983; | |||||
} | |||||
} | |||||
} | |||||
</style> | |||||
<script> | |||||
export default { | |||||
name: 'App' | |||||
}; | |||||
</script> | |||||
<style lang="scss"></style> |
@@ -0,0 +1,23 @@ | |||||
// axios | |||||
import request from '@/utils/request'; | |||||
export const APIUser = { | |||||
getUser, | |||||
getUserId | |||||
}; | |||||
export default APIUser; | |||||
function getUser(data) { | |||||
return request({ | |||||
url: `/api/User/UserInfo`, | |||||
method: 'post', | |||||
data | |||||
}); | |||||
} | |||||
export function getUserId(params) { | |||||
return request({ | |||||
url: `/api/User/UserInfo`, | |||||
method: 'get', | |||||
params | |||||
}); | |||||
} |
@@ -0,0 +1,4 @@ | |||||
// import qs from 'qs' | |||||
// axios | |||||
// import request from '@/utils/request' | |||||
// home api |
@@ -0,0 +1,7 @@ | |||||
const api = { | |||||
Login: '/user/login', | |||||
UserInfo: '/user/userinfo', | |||||
UserName: '/user/name' | |||||
}; | |||||
export default api; |
@@ -0,0 +1,32 @@ | |||||
import api from './index'; | |||||
// axios | |||||
import request from '@/utils/request'; | |||||
// 登录 | |||||
export function login(data) { | |||||
return request({ | |||||
url: api.Login, | |||||
method: 'post', | |||||
data | |||||
}); | |||||
} | |||||
// 用户信息 get 方法 | |||||
export function getUserInfo(data) { | |||||
return request({ | |||||
url: api.UserInfo, | |||||
method: 'post', | |||||
data, | |||||
hideloading: true | |||||
}); | |||||
} | |||||
// 用户名称 get 方法 | |||||
export function getUserName(params) { | |||||
return request({ | |||||
url: api.UserName, | |||||
method: 'get', | |||||
params, | |||||
hideloading: true | |||||
}); | |||||
} |
@@ -0,0 +1,13 @@ | |||||
@import './variables.scss'; | |||||
@import './mixin.scss'; | |||||
html, | |||||
body .app { | |||||
color: #333333; | |||||
font-family: Arial, Helvetica, 'STHeiti STXihei', 'Microsoft YaHei', Tohoma, sans-serif; | |||||
background-color: $background-color; | |||||
} | |||||
.app-container { | |||||
padding-bottom: 100px; | |||||
} |
@@ -0,0 +1,36 @@ | |||||
// mixin | |||||
// 清除浮动 | |||||
@mixin clearfix { | |||||
&:after { | |||||
content: ""; | |||||
display: table; | |||||
clear: both; | |||||
} | |||||
} | |||||
// 多行隐藏 | |||||
@mixin textoverflow($clamp:1) { | |||||
display: block; | |||||
overflow: hidden; | |||||
text-overflow: ellipsis; | |||||
-o-text-overflow: ellipsis; | |||||
display: -webkit-box; | |||||
-webkit-line-clamp: $clamp; | |||||
/*! autoprefixer: ignore next */ | |||||
-webkit-box-orient: vertical; | |||||
} | |||||
//flex box | |||||
@mixin flexbox($jc:space-between, $ai:center, $fd:row, $fw:nowrap) { | |||||
display: flex; | |||||
display: -webkit-flex; | |||||
flex: 1; | |||||
justify-content: $jc; | |||||
-webkit-justify-content: $jc; | |||||
align-items: $ai; | |||||
-webkit-align-items: $ai; | |||||
flex-direction: $fd; | |||||
-webkit-flex-direction: $fd; | |||||
flex-wrap: $fw; | |||||
-webkit-flex-wrap: $fw; | |||||
} |
@@ -0,0 +1,3 @@ | |||||
// variables | |||||
$background-color: #f8f8f8; |
@@ -1,60 +0,0 @@ | |||||
<template> | |||||
<div class="hello"> | |||||
<h1>{{ msg }}</h1> | |||||
<p> | |||||
For a guide and recipes on how to configure / customize this project,<br> | |||||
check out the | |||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>. | |||||
</p> | |||||
<h3>Installed CLI Plugins</h3> | |||||
<ul> | |||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li> | |||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li> | |||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li> | |||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li> | |||||
</ul> | |||||
<h3>Essential Links</h3> | |||||
<ul> | |||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li> | |||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li> | |||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li> | |||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li> | |||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li> | |||||
</ul> | |||||
<h3>Ecosystem</h3> | |||||
<ul> | |||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li> | |||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li> | |||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li> | |||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li> | |||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li> | |||||
</ul> | |||||
</div> | |||||
</template> | |||||
<script> | |||||
export default { | |||||
name: 'HelloWorld', | |||||
props: { | |||||
msg: String | |||||
} | |||||
} | |||||
</script> | |||||
<!-- Add "scoped" attribute to limit CSS to this component only --> | |||||
<style scoped lang="scss"> | |||||
h3 { | |||||
margin: 40px 0 0; | |||||
} | |||||
ul { | |||||
list-style-type: none; | |||||
padding: 0; | |||||
} | |||||
li { | |||||
display: inline-block; | |||||
margin: 0 10px; | |||||
} | |||||
a { | |||||
color: #42b983; | |||||
} | |||||
</style> |
@@ -0,0 +1,54 @@ | |||||
<template> | |||||
<div> | |||||
<van-tabbar fixed route v-model="active" @change="handleChange"> | |||||
<van-tabbar-item v-for="(item, index) in data" :to="item.to" :icon="item.icon" :key="index"> | |||||
{{ item.title }} | |||||
</van-tabbar-item> | |||||
</van-tabbar> | |||||
</div> | |||||
</template> | |||||
<script> | |||||
export default { | |||||
name: 'TabBar', | |||||
props: { | |||||
defaultActive: { | |||||
type: Number, | |||||
default: 0 | |||||
}, | |||||
data: { | |||||
type: Array, | |||||
default: () => { | |||||
return []; | |||||
} | |||||
} | |||||
}, | |||||
data() { | |||||
return { | |||||
active: this.defaultActive | |||||
}; | |||||
}, | |||||
methods: { | |||||
handleChange(value) { | |||||
this.$emit('change', value); | |||||
} | |||||
} | |||||
}; | |||||
</script> | |||||
<!-- Add "scoped" attribute to limit CSS to this component only --> | |||||
<style scoped> | |||||
h3 { | |||||
margin: 40px 0 0; | |||||
} | |||||
ul { | |||||
list-style-type: none; | |||||
padding: 0; | |||||
} | |||||
li { | |||||
display: inline-block; | |||||
margin: 0 10px; | |||||
} | |||||
a { | |||||
color: #42b983; | |||||
} | |||||
</style> |
@@ -0,0 +1,3 @@ | |||||
// 微信公众号AppId | |||||
const AppId = process.env.NODE_ENV === 'production' ? 'wx23f697736154110b' : 'wx5e26f0813859e5f6'; | |||||
export default AppId; |
@@ -0,0 +1,9 @@ | |||||
// 本地环境配置 | |||||
module.exports = { | |||||
title: 'vue-h5-template', | |||||
baseUrl: 'http://localhost:9018', // 项目地址 | |||||
baseApi: 'https://test.xxx.com/api', // 本地api请求地址,注意:如果你使用了代理,请设置成'/' | |||||
APPID: 'xxx', | |||||
APPSECRET: 'xxx', | |||||
$cdn: 'https://www.sunniejs.cn/static' | |||||
}; |
@@ -0,0 +1,9 @@ | |||||
// 正式 | |||||
module.exports = { | |||||
title: 'vue-h5-template', | |||||
baseUrl: 'https://www.xxx.com/', // 正式项目地址 | |||||
baseApi: 'https://www.xxx.com/api', // 正式api请求地址 | |||||
APPID: 'xxx', | |||||
APPSECRET: 'xxx', | |||||
$cdn: 'https://www.sunniejs.cn/static' | |||||
}; |
@@ -0,0 +1,8 @@ | |||||
module.exports = { | |||||
title: 'vue-h5-template', | |||||
baseUrl: 'https://test.xxx.com', // 测试项目地址 | |||||
baseApi: 'https://test.xxx.com/api', // 测试api请求地址 | |||||
APPID: 'xxx', | |||||
APPSECRET: 'xxx', | |||||
$cdn: 'https://www.sunniejs.cn/static' | |||||
}; |
@@ -0,0 +1,4 @@ | |||||
// 根据环境引入不同配置 process.env.VUE_APP_ENV | |||||
const environment = process.env.VUE_APP_ENV || 'production'; | |||||
const config = require('./env.' + environment); | |||||
module.exports = config; |
@@ -0,0 +1,2 @@ | |||||
// 项目版本号 | |||||
export const VersionModel = '1.0.0'; |
@@ -0,0 +1,37 @@ | |||||
/** | |||||
*格式化时间 | |||||
*yyyy-MM-dd hh:mm:ss | |||||
*/ | |||||
export function formatDate(time, fmt) { | |||||
if (time === undefined || '') { | |||||
return; | |||||
} | |||||
const date = new Date(time); | |||||
if (/(y+)/.test(fmt)) { | |||||
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); | |||||
} | |||||
const o = { | |||||
'M+': date.getMonth() + 1, | |||||
'd+': date.getDate(), | |||||
'h+': date.getHours(), | |||||
'm+': date.getMinutes(), | |||||
's+': date.getSeconds() | |||||
}; | |||||
for (const k in o) { | |||||
if (new RegExp(`(${k})`).test(fmt)) { | |||||
const str = o[k] + ''; | |||||
fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : padLeftZero(str)); | |||||
} | |||||
} | |||||
return fmt; | |||||
} | |||||
function padLeftZero(str) { | |||||
return ('00' + str).substr(str.length); | |||||
} | |||||
/* | |||||
* 隐藏用户手机号中间四位 | |||||
*/ | |||||
export function hidePhone(phone) { | |||||
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'); | |||||
} |
@@ -0,0 +1,7 @@ | |||||
import Vue from 'vue'; | |||||
import * as filter from './filter'; | |||||
Object.keys(filter).forEach(k => Vue.filter(k, filter[k])); | |||||
Vue.prototype.$formatDate = Vue.filter('formatDate'); | |||||
Vue.prototype.$hidePhone = Vue.filter('hidePhone'); |
@@ -1,12 +1,30 @@ | |||||
import Vue from 'vue' | |||||
import App from './App.vue' | |||||
import router from './router' | |||||
import store from './store' | |||||
// 兼容 IE | |||||
// https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md#babelpolyfill | |||||
import 'core-js/stable'; | |||||
import 'regenerator-runtime/runtime'; | |||||
Vue.config.productionTip = false | |||||
import Vue from 'vue'; | |||||
import App from './App.vue'; | |||||
import router from './router'; | |||||
import store from './store'; | |||||
// 设置 js中可以访问 $cdn | |||||
import { $cdn } from '@/config'; | |||||
Vue.prototype.$cdn = $cdn; | |||||
// 全局引入按需引入UI库 vant | |||||
import '@/plugins/vant'; | |||||
// 引入全局样式 | |||||
import '@/assets/css/index.scss'; | |||||
// 移动端适配 | |||||
import 'amfe-flexible'; | |||||
// filters | |||||
import './filters'; | |||||
Vue.config.productionTip = false; | |||||
new Vue({ | new Vue({ | ||||
el: '#app', | |||||
router, | router, | ||||
store, | store, | ||||
render: h => h(App) | render: h => h(App) | ||||
}).$mount('#app') | |||||
}); |
@@ -0,0 +1,4 @@ | |||||
// 按需全局引入 vant组件 | |||||
import Vue from 'vue'; | |||||
import { Button, Tabbar, TabbarItem, Toast } from 'vant'; | |||||
Vue.use(Button).use(Tabbar).use(TabbarItem).use(Toast); |
@@ -1,27 +1,30 @@ | |||||
import Vue from 'vue' | |||||
import VueRouter from 'vue-router' | |||||
import HomeView from '../views/HomeView.vue' | |||||
import Vue from 'vue'; | |||||
import Router from 'vue-router'; | |||||
import { constantRouterMap } from './router.config.js'; | |||||
Vue.use(VueRouter) | |||||
// hack router push callback | |||||
const originalPush = Router.prototype.push; | |||||
Router.prototype.push = function push(location, onResolve, onReject) { | |||||
if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject); | |||||
return originalPush.call(this, location).catch(err => err); | |||||
}; | |||||
const routes = [ | |||||
{ | |||||
path: '/', | |||||
name: 'home', | |||||
component: HomeView | |||||
}, | |||||
{ | |||||
path: '/about', | |||||
name: 'about', | |||||
// route level code-splitting | |||||
// this generates a separate chunk (about.[hash].js) for this route | |||||
// which is lazy-loaded when the route is visited. | |||||
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue') | |||||
} | |||||
] | |||||
Vue.use(Router); | |||||
const router = new VueRouter({ | |||||
routes | |||||
}) | |||||
const createRouter = () => | |||||
new Router({ | |||||
// mode: 'history', // 如果你是 history模式 需要配置vue.config.js publicPath | |||||
// base: process.env.BASE_URL, | |||||
scrollBehavior: () => ({ y: 0 }), | |||||
routes: constantRouterMap | |||||
}); | |||||
export default router | |||||
const router = createRouter(); | |||||
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 | |||||
export function resetRouter() { | |||||
const newRouter = createRouter(); | |||||
router.matcher = newRouter.matcher; // reset router | |||||
} | |||||
export default router; |
@@ -0,0 +1,29 @@ | |||||
/** | |||||
* 基础路由 | |||||
* @type { *[] } | |||||
*/ | |||||
export const constantRouterMap = [ | |||||
{ | |||||
path: '/', | |||||
component: () => import('@/views/layouts/index'), | |||||
redirect: '/home', | |||||
meta: { | |||||
title: '首页', | |||||
keepAlive: false | |||||
}, | |||||
children: [ | |||||
{ | |||||
path: '/home', | |||||
name: 'Home', | |||||
component: () => import('@/views/HomeView'), | |||||
meta: { title: '首页', keepAlive: false } | |||||
}, | |||||
{ | |||||
path: '/about', | |||||
name: 'About', | |||||
component: () => import('@/views/AboutView'), | |||||
meta: { title: '关于我', keepAlive: false } | |||||
} | |||||
] | |||||
} | |||||
]; |
@@ -0,0 +1,12 @@ | |||||
const getters = { | |||||
/* userName: state => { | |||||
if (state.app.userName != '') { | |||||
return state.app.userName; | |||||
} else { | |||||
return window.localStorage['userName'] == null ? '' : window.localStorage['userName']; | |||||
} | |||||
}, */ | |||||
userName: state => state.app.userName, | |||||
testName: state => state.app.testName | |||||
}; | |||||
export default getters; |
@@ -1,17 +1,15 @@ | |||||
import Vue from 'vue' | |||||
import Vuex from 'vuex' | |||||
import Vue from 'vue'; | |||||
import Vuex from 'vuex'; | |||||
import getters from './getters'; | |||||
import app from './modules/app'; | |||||
Vue.use(Vuex) | |||||
Vue.use(Vuex); | |||||
export default new Vuex.Store({ | |||||
state: { | |||||
}, | |||||
getters: { | |||||
}, | |||||
mutations: { | |||||
}, | |||||
actions: { | |||||
}, | |||||
const store = new Vuex.Store({ | |||||
modules: { | modules: { | ||||
} | |||||
}) | |||||
app | |||||
}, | |||||
getters | |||||
}); | |||||
export default store; |
@@ -0,0 +1,28 @@ | |||||
const state = { | |||||
userName: '', | |||||
testName: '' | |||||
}; | |||||
const mutations = { | |||||
SET_USER_NAME(state, name) { | |||||
state.userName = name; | |||||
window.localStorage['userName'] = name; | |||||
}, | |||||
SET_USER_TEST_NAME(state, name) { | |||||
state.testName = name; | |||||
window.localStorage['testName'] = name; | |||||
} | |||||
}; | |||||
const actions = { | |||||
// 设置name | |||||
setUserName({ commit }, name) { | |||||
commit('SET_USER_NAME', name); | |||||
}, | |||||
setTestName({ commit }, name) { | |||||
commit('SET_USER_TEST_NAME', name); | |||||
} | |||||
}; | |||||
export default { | |||||
state, | |||||
mutations, | |||||
actions | |||||
}; |
@@ -0,0 +1,98 @@ | |||||
/** | |||||
* Created by PanJiaChen on 16/11/18. | |||||
*/ | |||||
/** | |||||
* Parse the time to string | |||||
* @param {(Object|string|number)} time | |||||
* @param {string} cFormat | |||||
* @returns {string} | |||||
*/ | |||||
export function parseTime(time, cFormat) { | |||||
if (arguments.length === 0) { | |||||
return null; | |||||
} | |||||
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'; | |||||
let date; | |||||
if (typeof time === 'object') { | |||||
date = time; | |||||
} else { | |||||
if (typeof time === 'string' && /^[0-9]+$/.test(time)) { | |||||
time = parseInt(time); | |||||
} | |||||
if (typeof time === 'number' && time.toString().length === 10) { | |||||
time = time * 1000; | |||||
} | |||||
date = new Date(time); | |||||
} | |||||
const formatObj = { | |||||
y: date.getFullYear(), | |||||
m: date.getMonth() + 1, | |||||
d: date.getDate(), | |||||
h: date.getHours(), | |||||
i: date.getMinutes(), | |||||
s: date.getSeconds(), | |||||
a: date.getDay() | |||||
}; | |||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { | |||||
let value = formatObj[key]; | |||||
// Note: getDay() returns 0 on Sunday | |||||
if (key === 'a') { | |||||
return ['日', '一', '二', '三', '四', '五', '六'][value]; | |||||
} | |||||
if (result.length > 0 && value < 10) { | |||||
value = '0' + value; | |||||
} | |||||
return value || 0; | |||||
}); | |||||
return time_str; | |||||
} | |||||
/** | |||||
* @param {number} time | |||||
* @param {string} option | |||||
* @returns {string} | |||||
*/ | |||||
export function formatTime(time, option) { | |||||
if (('' + time).length === 10) { | |||||
time = parseInt(time) * 1000; | |||||
} else { | |||||
time = +time; | |||||
} | |||||
const d = new Date(time); | |||||
const now = Date.now(); | |||||
const diff = (now - d) / 1000; | |||||
if (diff < 30) { | |||||
return '刚刚'; | |||||
} else if (diff < 3600) { | |||||
// less 1 hour | |||||
return Math.ceil(diff / 60) + '分钟前'; | |||||
} else if (diff < 3600 * 24) { | |||||
return Math.ceil(diff / 3600) + '小时前'; | |||||
} else if (diff < 3600 * 24 * 2) { | |||||
return '1天前'; | |||||
} | |||||
if (option) { | |||||
return parseTime(time, option); | |||||
} else { | |||||
return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'; | |||||
} | |||||
} | |||||
/** | |||||
* @param {string} url | |||||
* @returns {Object} | |||||
*/ | |||||
export function param2Obj(url) { | |||||
const search = url.split('?')[1]; | |||||
if (!search) { | |||||
return {}; | |||||
} | |||||
return JSON.parse( | |||||
'{"' + | |||||
decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"').replace(/\+/g, ' ') + | |||||
'"}' | |||||
); | |||||
} |
@@ -0,0 +1,58 @@ | |||||
import axios from 'axios'; | |||||
import store from '@/store'; | |||||
import { Toast } from 'vant'; | |||||
// 根据环境不同引入不同api地址 | |||||
import { baseApi } from '@/config'; | |||||
// create an axios instance | |||||
const service = axios.create({ | |||||
baseURL: baseApi, // url = base api url + request url | |||||
withCredentials: true, // send cookies when cross-domain requests | |||||
timeout: 5000 // request timeout | |||||
}); | |||||
// request拦截器 request interceptor | |||||
service.interceptors.request.use( | |||||
config => { | |||||
// 不传递默认开启loading | |||||
if (!config.hideloading) { | |||||
// loading | |||||
Toast.loading({ | |||||
forbidClick: true | |||||
}); | |||||
} | |||||
if (store.getters.token) { | |||||
config.headers['X-Token'] = ''; | |||||
} | |||||
return config; | |||||
}, | |||||
error => { | |||||
// do something with request error | |||||
console.log(error); // for debug | |||||
return Promise.reject(error); | |||||
} | |||||
); | |||||
// respone拦截器 | |||||
service.interceptors.response.use( | |||||
response => { | |||||
Toast.clear(); | |||||
const res = response.data; | |||||
if (res.status && res.status !== 200) { | |||||
// 登录超时,重新登录 | |||||
if (res.status === 401) { | |||||
store.dispatch('FedLogOut').then(() => { | |||||
location.reload(); | |||||
}); | |||||
} | |||||
return Promise.reject(res || 'error'); | |||||
} else { | |||||
return Promise.resolve(res); | |||||
} | |||||
}, | |||||
error => { | |||||
Toast.clear(); | |||||
console.log('err' + error); // for debug | |||||
return Promise.reject(error); | |||||
} | |||||
); | |||||
export default service; |
@@ -0,0 +1,20 @@ | |||||
/** | |||||
* Created by Sunnie on 19/06/04. | |||||
*/ | |||||
/** | |||||
* @param {string} path | |||||
* @returns {Boolean} | |||||
*/ | |||||
export function isExternal(path) { | |||||
return /^(https?:|mailto:|tel:)/.test(path); | |||||
} | |||||
/** | |||||
* @param {string} str | |||||
* @returns {Boolean} | |||||
*/ | |||||
export function validUsername(str) { | |||||
const valid_map = ['admin', 'editor']; | |||||
return valid_map.indexOf(str.trim()) >= 0; | |||||
} |
@@ -1,18 +1,29 @@ | |||||
<template> | <template> | ||||
<div class="home"> | |||||
<img alt="Vue logo" src="../assets/logo.png"> | |||||
<HelloWorld msg="Welcome to Your Vue.js App"/> | |||||
<div> | |||||
<van-button @click="onSave" type="warning">测试</van-button> | |||||
</div> | </div> | ||||
</template> | </template> | ||||
<script> | <script> | ||||
// @ is an alias to /src | |||||
import HelloWorld from '@/components/HelloWorld.vue' | |||||
export default { | export default { | ||||
name: 'HomeView', | |||||
components: { | |||||
HelloWorld | |||||
data() { | |||||
return {}; | |||||
}, | |||||
created() { | |||||
this.code = 'daa'; | |||||
}, | |||||
mounted() {}, | |||||
methods: { | |||||
onSave() { | |||||
this.$store.dispatch('setTestName', '测试撒法发啊啊'); | |||||
console.log(this.$store.getters.testName); | |||||
this.$toast.success({ | |||||
message: `${this.$store.getters.testName}` | |||||
}); | |||||
} | |||||
} | } | ||||
} | |||||
}; | |||||
</script> | </script> | ||||
<style scoped> | |||||
/* @import url(); 引入css类 */ | |||||
</style> |
@@ -0,0 +1,48 @@ | |||||
<template> | |||||
<div class="app-container"> | |||||
<div class="layout-content"> | |||||
<keep-alive v-if="$route.meta.keepAlive"> | |||||
<router-view></router-view> | |||||
</keep-alive> | |||||
<router-view v-else></router-view> | |||||
</div> | |||||
<div class="layout-footer"> | |||||
<TabBar :data="tabbars" @change="handleChange" /> | |||||
</div> | |||||
</div> | |||||
</template> | |||||
<script> | |||||
import TabBar from '@/components/TabBar'; | |||||
export default { | |||||
name: 'AppLayout', | |||||
data() { | |||||
return { | |||||
tabbars: [ | |||||
{ | |||||
title: '首页', | |||||
to: { | |||||
name: 'Home' | |||||
}, | |||||
icon: 'home-o' | |||||
}, | |||||
{ | |||||
title: '关于我', | |||||
to: { | |||||
name: 'About' | |||||
}, | |||||
icon: 'user-o' | |||||
} | |||||
] | |||||
}; | |||||
}, | |||||
components: { | |||||
TabBar | |||||
}, | |||||
methods: { | |||||
handleChange(v) { | |||||
console.log('tab value:', v); | |||||
} | |||||
} | |||||
}; | |||||
</script> |
@@ -1,4 +1,172 @@ | |||||
const { defineConfig } = require('@vue/cli-service') | |||||
const path = require('path'); | |||||
const defaultSettings = require('./src/config/index.js'); | |||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; | |||||
const resolve = dir => path.join(__dirname, dir); | |||||
// page title | |||||
const name = defaultSettings.title || 'vue mobile template'; | |||||
// 生产环境,测试和正式 | |||||
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV); | |||||
const { defineConfig } = require('@vue/cli-service'); | |||||
// externals | |||||
// const externals = { | |||||
// vue: 'Vue', | |||||
// 'vue-router': 'VueRouter', | |||||
// vuex: 'Vuex', | |||||
// vant: 'vant', | |||||
// axios: 'axios' | |||||
// } | |||||
// CDN外链,会插入到index.html中 | |||||
// const cdn = { | |||||
// // 开发环境 | |||||
// dev: { | |||||
// css: [], | |||||
// js: [] | |||||
// }, | |||||
// // 生产环境 | |||||
// build: { | |||||
// css: ['https://cdn.jsdelivr.net/npm/vant@2.4.7/lib/index.css'], | |||||
// js: [ | |||||
// 'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js', | |||||
// 'https://cdn.jsdelivr.net/npm/vue-router@3.1.5/dist/vue-router.min.js', | |||||
// 'https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js', | |||||
// 'https://cdn.jsdelivr.net/npm/vuex@3.1.2/dist/vuex.min.js', | |||||
// 'https://cdn.jsdelivr.net/npm/vant@2.4.7/lib/index.min.js' | |||||
// ] | |||||
// } | |||||
// } | |||||
module.exports = defineConfig({ | module.exports = defineConfig({ | ||||
transpileDependencies: true | |||||
}) | |||||
publicPath: './', // 署应用包时的基本 URL。 vue-router hash 模式使用 | |||||
// publicPath: '/app/', //署应用包时的基本 URL。 vue-router history模式使用 | |||||
outputDir: 'dist', // 生产环境构建文件的目录 | |||||
assetsDir: 'static', // outputDir的静态资源(js、css、img、fonts)目录 | |||||
lintOnSave: !IS_PROD, | |||||
productionSourceMap: false, // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。 | |||||
devServer: { | |||||
port: 9020, // 端口 | |||||
open: false, // 启动后打开浏览器 | |||||
client: { | |||||
overlay: { | |||||
// 当出现编译器错误或警告时,在浏览器中显示全屏覆盖层 | |||||
warnings: false, | |||||
errors: true | |||||
} | |||||
} | |||||
// proxy: { | |||||
// //配置跨域 | |||||
// '/api': { | |||||
// target: "https://test.xxx.com", | |||||
// // ws:true, | |||||
// changOrigin:true, | |||||
// pathRewrite:{ | |||||
// '^/api':'/' | |||||
// } | |||||
// } | |||||
// } | |||||
}, | |||||
css: { | |||||
extract: IS_PROD, // 是否将组件中的 CSS 提取至一个独立的 CSS 文件中 (而不是动态注入到 JavaScript 中的 inline 代码)。 | |||||
sourceMap: false, | |||||
loaderOptions: { | |||||
scss: { | |||||
// 向全局sass样式传入共享的全局变量, $src可以配置图片cdn前缀 | |||||
// 详情: https://cli.vuejs.org/guide/css.html#passing-options-to-pre-processor-loaders | |||||
additionalData: ` | |||||
@import "assets/css/mixin.scss"; | |||||
@import "assets/css/variables.scss"; | |||||
$cdn: "${defaultSettings.$cdn}"; | |||||
` | |||||
} | |||||
} | |||||
}, | |||||
configureWebpack: config => { | |||||
config.name = name; | |||||
// 为生产环境修改配置... | |||||
// if (IS_PROD) { | |||||
// // externals | |||||
// config.externals = externals | |||||
// } | |||||
}, | |||||
chainWebpack: config => { | |||||
config.plugins.delete('preload'); // TODO: need test | |||||
config.plugins.delete('prefetch'); // TODO: need test | |||||
// 别名 alias | |||||
config.resolve.alias | |||||
.set('@', resolve('src')) | |||||
.set('assets', resolve('src/assets')) | |||||
.set('api', resolve('src/api')) | |||||
.set('views', resolve('src/views')) | |||||
.set('components', resolve('src/components')); | |||||
/** | |||||
* 添加CDN参数到htmlWebpackPlugin配置中 | |||||
*/ | |||||
// config.plugin('html').tap(args => { | |||||
// if (IS_PROD) { | |||||
// args[0].cdn = cdn.build | |||||
// } else { | |||||
// args[0].cdn = cdn.dev | |||||
// } | |||||
// return args | |||||
// }) | |||||
/** | |||||
* 设置保留空格 | |||||
*/ | |||||
config.module | |||||
.rule('vue') | |||||
.use('vue-loader') | |||||
.loader('vue-loader') | |||||
.tap(options => { | |||||
options.compilerOptions.preserveWhitespace = true; | |||||
return options; | |||||
}) | |||||
.end(); | |||||
/** | |||||
* 打包分析 | |||||
*/ | |||||
if (IS_PROD) { | |||||
config.plugin('webpack-report').use(BundleAnalyzerPlugin, [ | |||||
{ | |||||
analyzerMode: 'static' | |||||
} | |||||
]); | |||||
} | |||||
config | |||||
// https://webpack.js.org/configuration/devtool/#development | |||||
.when(!IS_PROD, config => config.devtool('cheap-source-map')); | |||||
config.when(IS_PROD, config => { | |||||
config.optimization.splitChunks({ | |||||
chunks: 'all', | |||||
cacheGroups: { | |||||
// cacheGroups 下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块 | |||||
commons: { | |||||
name: 'chunk-commons', | |||||
test: resolve('src/components'), | |||||
minChunks: 3, // 被至少用三次以上打包分离 | |||||
priority: 5, // 优先级 | |||||
reuseExistingChunk: true // 表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。 | |||||
}, | |||||
node_vendors: { | |||||
name: 'chunk-libs', | |||||
chunks: 'initial', // 只打包初始时依赖的第三方 | |||||
test: /[\\/]node_modules[\\/]/, | |||||
priority: 10 | |||||
}, | |||||
vantUI: { | |||||
name: 'chunk-vantUI', // 单独将 vantUI 拆包 | |||||
priority: 20, // 数字大权重到,满足多个 cacheGroups 的条件时候分到权重高的 | |||||
test: /[\\/]node_modules[\\/]_?vant(.*)/ | |||||
} | |||||
} | |||||
}); | |||||
config.optimization.runtimeChunk('single'); | |||||
}); | |||||
} | |||||
}); |