@@ -1,5 +1,14 @@ | |||
[*.{js,jsx,ts,tsx,vue}] | |||
# http://editorconfig.org | |||
root = true | |||
[*] | |||
charset = utf-8 | |||
indent_style = space | |||
indent_size = 2 | |||
trim_trailing_whitespace = true | |||
end_of_line = lf | |||
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: { | |||
node: true | |||
}, | |||
extends: [ | |||
'plugin:vue/essential', | |||
'@vue/standard' | |||
], | |||
extends: ['plugin:vue/essential', 'eslint:recommended', 'plugin:prettier/recommended'], | |||
parserOptions: { | |||
parser: '@babel/eslint-parser' | |||
}, | |||
rules: { | |||
'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 = { | |||
'*.{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, | |||
"scripts": { | |||
"serve": "vue-cli-service serve", | |||
"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": { | |||
"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" | |||
}, | |||
"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> | |||
<html lang=""> | |||
<html> | |||
<head> | |||
<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"> | |||
<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> | |||
<body> | |||
<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> | |||
<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 --> | |||
</body> | |||
</html> |
@@ -1,32 +1,11 @@ | |||
<template> | |||
<div id="app"> | |||
<nav> | |||
<router-link to="/">Home</router-link> | | |||
<router-link to="/about">About</router-link> | |||
</nav> | |||
<router-view/> | |||
<router-view /> | |||
</div> | |||
</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({ | |||
el: '#app', | |||
router, | |||
store, | |||
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: { | |||
} | |||
}) | |||
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> | |||
<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> | |||
</template> | |||
<script> | |||
// @ is an alias to /src | |||
import HelloWorld from '@/components/HelloWorld.vue' | |||
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> | |||
<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({ | |||
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'); | |||
}); | |||
} | |||
}); |