ソースを参照

初始化项目

develop
chenJinxu 11ヶ月前
コミット
4e2f681ac8
43個のファイルの変更1370行の追加1728行の削除
  1. +11
    -2
      .editorconfig
  2. +4
    -0
      .env.development
  3. +4
    -0
      .env.production
  4. +4
    -0
      .env.staging
  5. +6
    -6
      .eslintrc.js
  6. +19
    -0
      .postcssrc.js
  7. +24
    -0
      .prettierrc
  8. +21
    -3
      babel.config.js
  9. +1
    -1
      lint-staged.config.js
  10. +418
    -1541
      package-lock.json
  11. +34
    -26
      package.json
  12. +15
    -5
      public/index.html
  13. +7
    -28
      src/App.vue
  14. +23
    -0
      src/api/demo.js
  15. +4
    -0
      src/api/home.js
  16. +7
    -0
      src/api/index.js
  17. +32
    -0
      src/api/user.js
  18. +13
    -0
      src/assets/css/index.scss
  19. +36
    -0
      src/assets/css/mixin.scss
  20. +3
    -0
      src/assets/css/variables.scss
  21. +0
    -60
      src/components/HelloWorld.vue
  22. +54
    -0
      src/components/TabBar.vue
  23. +3
    -0
      src/config/appId.js
  24. +9
    -0
      src/config/env.development.js
  25. +9
    -0
      src/config/env.production.js
  26. +8
    -0
      src/config/env.staging.js
  27. +4
    -0
      src/config/index.js
  28. +2
    -0
      src/config/models.js
  29. +37
    -0
      src/filters/filter.js
  30. +7
    -0
      src/filters/index.js
  31. +24
    -6
      src/main.js
  32. +4
    -0
      src/plugins/vant.js
  33. +26
    -23
      src/router/index.js
  34. +29
    -0
      src/router/router.config.js
  35. +12
    -0
      src/store/getters.js
  36. +12
    -14
      src/store/index.js
  37. +28
    -0
      src/store/modules/app.js
  38. +98
    -0
      src/utils/index.js
  39. +58
    -0
      src/utils/request.js
  40. +20
    -0
      src/utils/validate.js
  41. +21
    -10
      src/views/HomeView.vue
  42. +48
    -0
      src/views/layouts/index.vue
  43. +171
    -3
      vue.config.js

+ 11
- 2
.editorconfig ファイルの表示

@@ -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

+ 4
- 0
.env.development ファイルの表示

@@ -0,0 +1,4 @@
NODE_ENV='development'
# must start with VUE_APP_
VUE_APP_ENV = 'development'

+ 4
- 0
.env.production ファイルの表示

@@ -0,0 +1,4 @@
NODE_ENV='production'
# must start with VUE_APP_
VUE_APP_ENV = 'production'

+ 4
- 0
.env.staging ファイルの表示

@@ -0,0 +1,4 @@
NODE_ENV='production'
# must start with VUE_APP_
VUE_APP_ENV = 'staging'

+ 6
- 6
.eslintrc.js ファイルの表示

@@ -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'
}
}
};

+ 19
- 0
.postcssrc.js ファイルの表示

@@ -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`
}
}
};
};

+ 24
- 0
.prettierrc ファイルの表示

@@ -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"
}

+ 21
- 3
babel.config.js ファイルの表示

@@ -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
- 1
lint-staged.config.js ファイルの表示

@@ -1,3 +1,3 @@
module.exports = {
'*.{js,jsx,vue}': 'vue-cli-service lint'
}
};

+ 418
- 1541
package-lock.json
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 34
- 26
package.json ファイルの表示

@@ -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"
}
}

+ 15
- 5
public/index.html ファイルの表示

@@ -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>

+ 7
- 28
src/App.vue ファイルの表示

@@ -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>

+ 23
- 0
src/api/demo.js ファイルの表示

@@ -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
});
}

+ 4
- 0
src/api/home.js ファイルの表示

@@ -0,0 +1,4 @@
// import qs from 'qs'
// axios
// import request from '@/utils/request'
// home api

+ 7
- 0
src/api/index.js ファイルの表示

@@ -0,0 +1,7 @@
const api = {
Login: '/user/login',
UserInfo: '/user/userinfo',
UserName: '/user/name'
};

export default api;

+ 32
- 0
src/api/user.js ファイルの表示

@@ -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
});
}

+ 13
- 0
src/assets/css/index.scss ファイルの表示

@@ -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;
}

+ 36
- 0
src/assets/css/mixin.scss ファイルの表示

@@ -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;
}

+ 3
- 0
src/assets/css/variables.scss ファイルの表示

@@ -0,0 +1,3 @@
// variables
$background-color: #f8f8f8;

+ 0
- 60
src/components/HelloWorld.vue ファイルの表示

@@ -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>

+ 54
- 0
src/components/TabBar.vue ファイルの表示

@@ -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>

+ 3
- 0
src/config/appId.js ファイルの表示

@@ -0,0 +1,3 @@
// 微信公众号AppId
const AppId = process.env.NODE_ENV === 'production' ? 'wx23f697736154110b' : 'wx5e26f0813859e5f6';
export default AppId;

+ 9
- 0
src/config/env.development.js ファイルの表示

@@ -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'
};

+ 9
- 0
src/config/env.production.js ファイルの表示

@@ -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'
};

+ 8
- 0
src/config/env.staging.js ファイルの表示

@@ -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'
};

+ 4
- 0
src/config/index.js ファイルの表示

@@ -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;

+ 2
- 0
src/config/models.js ファイルの表示

@@ -0,0 +1,2 @@
// 项目版本号
export const VersionModel = '1.0.0';

+ 37
- 0
src/filters/filter.js ファイルの表示

@@ -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');
}

+ 7
- 0
src/filters/index.js ファイルの表示

@@ -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');

+ 24
- 6
src/main.js ファイルの表示

@@ -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')
});

+ 4
- 0
src/plugins/vant.js ファイルの表示

@@ -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);

+ 26
- 23
src/router/index.js ファイルの表示

@@ -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;

+ 29
- 0
src/router/router.config.js ファイルの表示

@@ -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 }
}
]
}
];

+ 12
- 0
src/store/getters.js ファイルの表示

@@ -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;

+ 12
- 14
src/store/index.js ファイルの表示

@@ -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;

+ 28
- 0
src/store/modules/app.js ファイルの表示

@@ -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
};

+ 98
- 0
src/utils/index.js ファイルの表示

@@ -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, ' ') +
'"}'
);
}

+ 58
- 0
src/utils/request.js ファイルの表示

@@ -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;

+ 20
- 0
src/utils/validate.js ファイルの表示

@@ -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;
}

+ 21
- 10
src/views/HomeView.vue ファイルの表示

@@ -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>

+ 48
- 0
src/views/layouts/index.vue ファイルの表示

@@ -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>

+ 171
- 3
vue.config.js ファイルの表示

@@ -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');
});
}
});

読み込み中…
キャンセル
保存