feat:init version 2.0.0
@ -0,0 +1,2 @@
|
||||
> 1%
|
||||
last 2 versions
|
@ -0,0 +1,5 @@
|
||||
[*.{js,jsx,ts,tsx,vue}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
@ -0,0 +1,14 @@
|
||||
# just a flag
|
||||
ENV = 'development'
|
||||
|
||||
# base api
|
||||
VUE_APP_BASE_API = '/dev-api'
|
||||
|
||||
# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,
|
||||
# to control whether the babel-plugin-dynamic-import-node plugin is enabled.
|
||||
# It only does one thing by converting all import() to require().
|
||||
# This configuration can significantly increase the speed of hot updates,
|
||||
# when you have a large number of pages.
|
||||
# Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js
|
||||
|
||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
@ -0,0 +1,6 @@
|
||||
# just a flag
|
||||
ENV = 'production'
|
||||
|
||||
# base api
|
||||
VUE_APP_BASE_API = '/prod-api'
|
||||
|
@ -0,0 +1,4 @@
|
||||
build/*.js
|
||||
src/assets
|
||||
public
|
||||
dist
|
@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/essential',
|
||||
'@vue/standard'
|
||||
],
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint'
|
||||
},
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
"space-before-function-paren": 0,
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Ewall&熊猫
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "panda-vue-template",
|
||||
"version": "0.1.0",
|
||||
"author": "Ewall&熊猫",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"vue",
|
||||
"vue-template",
|
||||
"panda-vue-template"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Ewall1106/panda-vue-template"
|
||||
},
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
"core-js": "^3.6.4",
|
||||
"js-cookie": "^2.2.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
"vant": "^2.5.5",
|
||||
"vue": "^2.6.11",
|
||||
"vue-router": "^3.1.5",
|
||||
"vue-scrollto": "^2.17.1",
|
||||
"vuex": "^3.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.2.0",
|
||||
"@vue/cli-plugin-eslint": "~4.2.0",
|
||||
"@vue/cli-plugin-router": "~4.2.0",
|
||||
"@vue/cli-plugin-vuex": "~4.2.0",
|
||||
"@vue/cli-service": "~4.2.0",
|
||||
"@vue/eslint-config-standard": "^5.1.0",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-plugin-import": "^1.13.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"eslint-plugin-node": "^11.0.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-standard": "^4.0.0",
|
||||
"eslint-plugin-vue": "^6.1.2",
|
||||
"less": "^3.11.1",
|
||||
"less-loader": "^5.0.0",
|
||||
"node-sass": "^4.13.1",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"postcss-px-to-viewport": "^1.1.1",
|
||||
"sass-loader": "^8.0.2",
|
||||
"svg-sprite-loader": "^4.2.1",
|
||||
"svgo": "^1.3.2",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
// https://github.com/csstools/postcss-preset-env
|
||||
'postcss-preset-env': {},
|
||||
// https://github.com/evrone/postcss-px-to-viewport/blob/master/README_CN.md
|
||||
'postcss-px-to-viewport': {
|
||||
unitToConvert: 'px',
|
||||
viewportWidth: 750,
|
||||
unitPrecision: 3,
|
||||
propList: ['*'],
|
||||
viewportUnit: 'vw',
|
||||
fontViewportUnit: 'vw',
|
||||
selectorBlackList: ['.ignore', 'van'],
|
||||
minPixelValue: 1,
|
||||
mediaQuery: false,
|
||||
replace: true,
|
||||
exclude: [],
|
||||
landscape: false,
|
||||
landscapeUnit: 'vw',
|
||||
landscapeWidth: 568
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<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, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></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>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,16 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getInfo() {
|
||||
return request({
|
||||
url: '/user/info',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function login(data) {
|
||||
return request({
|
||||
url: '/user/login',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
After Width: | Height: | Size: 17 KiB |
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div class="back-top" v-show="isShow">
|
||||
<span v-scroll-to="'body'">
|
||||
<svg-icon icon-class="backtop" :width="30" :height="30"/>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import VueScrollTo from 'vue-scrollto'
|
||||
|
||||
Vue.use(VueScrollTo, {
|
||||
container: 'body',
|
||||
duration: 500,
|
||||
easing: 'ease',
|
||||
offset: 0,
|
||||
force: true,
|
||||
cancelable: true,
|
||||
onStart: false,
|
||||
onDone: false,
|
||||
onCancel: false,
|
||||
x: false,
|
||||
y: true
|
||||
})
|
||||
|
||||
export default {
|
||||
props: {
|
||||
offsetTop: {
|
||||
type: Number,
|
||||
default: 100
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
timer: null,
|
||||
isShow: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('scroll', this.handleScroll)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('scroll', this.handleScroll)
|
||||
},
|
||||
methods: {
|
||||
handleScroll() {
|
||||
if (this.timer) clearTimeout(this.timer)
|
||||
this.timer = setTimeout(() => {
|
||||
const scrollTop =
|
||||
window.pageYOffset ||
|
||||
document.documentElement.scrollTop ||
|
||||
document.body.scrollTop
|
||||
console.log('asss', scrollTop)
|
||||
this.isShow = scrollTop > this.offsetTop
|
||||
}, 15)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.back-top {
|
||||
position: fixed;
|
||||
right: 24px;
|
||||
bottom: 296px;
|
||||
z-index: 999;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<svg
|
||||
:class="svgClass"
|
||||
aria-hidden="true"
|
||||
v-on="$listeners"
|
||||
:width="width ? width : size"
|
||||
:height="height ? height : size"
|
||||
>
|
||||
<use :href="iconName" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SvgIcon',
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: '28px'
|
||||
},
|
||||
width: Number,
|
||||
height: Number
|
||||
},
|
||||
computed: {
|
||||
iconName() {
|
||||
return `#icon-${this.iconClass}`
|
||||
},
|
||||
svgClass() {
|
||||
if (this.className) {
|
||||
return 'svg-icon ' + this.className
|
||||
} else {
|
||||
return 'svg-icon'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.svg-icon {
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,9 @@
|
||||
import Vue from 'vue'
|
||||
import SvgIcon from '@/components/SvgIcon'
|
||||
|
||||
// register globally
|
||||
Vue.component('svg-icon', SvgIcon)
|
||||
|
||||
const req = require.context('./svg', false, /\.svg$/)
|
||||
const requireAll = requireContext => requireContext.keys().map(requireContext)
|
||||
requireAll(req)
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1584620151673" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1238" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M825.568 555.328l-287.392-289.28C531.808 259.648 523.488 256.576 515.2 256.64 514.08 256.544 513.12 256 512 256c-4.672 0-9.024 1.088-13.024 2.88-4.032 1.536-7.872 3.872-11.136 7.136l-259.328 258.88c-12.512 12.48-12.544 32.736-0.032 45.248 6.24 6.272 14.432 9.408 22.656 9.408 8.192 0 16.352-3.136 22.624-9.344L480 364.288 480 928c0 17.696 14.336 32 32 32s32-14.304 32-32L544 362.72l236.192 237.728c6.24 6.272 14.496 9.44 22.688 9.44s16.32-3.104 22.56-9.312C838.016 588.128 838.048 567.84 825.568 555.328z" p-id="1239"></path><path d="M864 192 160 192C142.336 192 128 177.664 128 160s14.336-32 32-32l704 0c17.696 0 32 14.336 32 32S881.696 192 864 192z" p-id="1240"></path></svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M18.448 57.545l-.244-.744-.198-.968-.132-.53v-2.181l.236-.859.24-.908.317-.953.428-1.06.561-1.103.794-1.104v-.773l.077-.724.123-.984.34-1.106.313-1.194.25-.548.289-.511.371-.569.405-.423v-2.73l.234-1.407.236-1.633.42-1.955.577-2.035.43-1.118.426-1.217.468-1.135.559-1.216.57-1.332.655-1.247.737-1.331.929-1.33.43-.762.457-.624.995-1.406 1.025-1.403 1.163-1.444 1.246-1.405 1.352-1.384 1.41-1.423 1.708-1.536 1.083-.934 1.322-1.008 1.34-.89 1.448-.855 1.392-.76 1.57-.63 1.667-.775 1.657-.532 1.653-.552 1.787-.548 1.785-.417 1.876-.347L59.128.68l1.879-.245 1.876-.252 2.002-.106h5.912l1.97.243 1.981.231 2.019.207 1.874.441 1.979.413 1.857.475 2.035.53 1.862.646 1.782.738 1.904.78 1.736.853 1.689.95 1.655 1.044 1.425.971.662.548.693.401 1.323 1.1 1.115 1.064 1.112 1.1 1.083 1.214.894 1.178 1.064 1.217.74 1.306.752 1.162.798 1.352.661 1.175 1.113 2.489.546 1.286.428 1.192.428 1.294.384 1.217.267 1.047.347 1.231.607 2.198.388 1.924.253 1.861.217 1.497.342 2.28.077.362.274.41.737 1.18.473.8.42.832.534.892.472 1.07.307 1.093.334 1.2.252 1.232.115.605.106.746v.648l-.106.643v.8l-.192.774-.35 1.5-.403.76-.299.852v.213l.142.264.4.623 1.746 2.53 1.377 1.9.66 1.267.889 1.389.774 1.52.893 1.627.894 1.828 1.006 2.069.567 1.268.518 1.239.447 1.307.44 1.175.336 1.235.342 1.16.432 2.261.343 2.31.235 2.05v2.891l-.158 1.025-.226 1.768-.308 1.59-.48 1.44-.18.588-.336.707-.28.493-.375.607-.33.383-.42.494-.375.4-.401.34-.48.207-.432.207-.355.114h-.543l-.346-.114-.66-.32-.302-.212-.317-.223-.347-.304-.35-.342-.579-.63-.684-.89-.539-.917-.538-.734-.526-.855-.741-1.517-.833-1.579-.098-.055h-.138l-.338.247-.196.415-.326.516-.567 1.533-.856 2.182-1.096 2.626-.824 1.308-.864 1.366-1.027 1.536-1.09 1.503-.557.68-.676.743-1.555 1.497.136.135.21.214.777.446 3.235 1.524 1.41.779 1.347.756 1.332.953 1.187.982.574.443.432.511.445.593.367.643.198.533.242.64.105.554.115.647-.115.433v.44l-.105.454-.242.415-.092.325-.22.394-.587.784-.543.627-.42.47-.35.348-.893.638-1.01.556-1.077.532-1.155.511-1.287.495-.693.207-.608.167-1.496.342-1.545.325-1.552.323-1.689.27-1.74.072-1.785.21h-5.539l-1.998-.114-1.86-.168-2.005-.27-1.99-.209-2.095-.286-2.03-.495-1.981-.374-1.968-.552-2.019-.707-1.98-.585-1.044-.342-.927-.323-.586-.223-.582-.12h-1.647l-1.904-.131-.962-.096-1.24-.135-.795.705-1.085.665-1.471.701-1.628.875-.99.475-1.033.376-2.281.914-1.24.305-1.3.343-1.803.344-1.13.086-1.193.1-1.246.135-1.45.053h-5.926l-3.346-.053-3.25-.321-1.644-.23-1.589-.23-1.546-.227-1.547-.305-1.442-.456-1.434-.325-1.294-.51-1.223-.474-1.142-.533-.99-.583-.984-.71-.336-.343-.44-.415-.334-.362-.3-.417-.278-.415-.215-.42-.311-.89-.109-.46-.138-.51v-.473l.138-.533v-.53l.109-.53v-1.069l.052-.564.259-.647.215-.646.39-.779.286-.3.236-.348.615-.738.49-.38.464-.266.428-.338.676-.21.543-.324.676-.341.77-.227.775-.231.897-.192.85-.11 1.008-.13 1.093-.081.284-.092h.063l.137-.115v-.13l-.2-.266-.58-.27-1.45-1.231-.975-.761-1.127-.967-1.136-1.082-1.181-1.382-1.36-1.558-.508-.843-.672-.87-.58-1.007-.522-1.1-.704-1.047-.459-1.194-.547-1.192-.546-1.33-.397-1.273-.378-1.575-.112-.057h-.115l-.059-.113h-.14l-.23.113-.114.057-.158.264-.057.321-.119.286-.206.477-.664 1.157-.345.701-.546.612-.58.736-.641.816-.677.724-.795.701-.734.658-.814.524-.89.546-.855.325-1.008.247-.99.095h-.233l-.228-.095-.18-.384-.29-.188-.38-.912-.237-.493-.255-.707-.21-.734-.113-.724-.313-1.648-.12-.972v-3.185l.12-2.379.196-1.214.23-1.252.21-1.347.374-1.254.42-1.443.431-1.407.578-1.448.545-1.38.754-1.4.699-1.52.855-1.425 1.006-1.538 1.023-1.382 1.069-1.538.891-1.071 1.142-1.227 1.202-1.237.56-.59.678-.662.985-.836 1.012-.853 1.647-1.446 1.242-.889z"/></svg>
|
After Width: | Height: | Size: 3.6 KiB |
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M124.884 109.812L94.256 79.166c-.357-.357-.757-.629-1.129-.914a50.366 50.366 0 008.186-27.59C101.327 22.689 78.656 0 50.67 0 22.685 0 0 22.688 0 50.663c0 27.989 22.685 50.663 50.656 50.663 10.186 0 19.643-3.03 27.6-8.201.286.385.557.771.9 1.114l30.628 30.632a10.633 10.633 0 007.543 3.129c2.728 0 5.457-1.043 7.543-3.115 4.171-4.157 4.171-10.915.014-15.073M50.671 85.338C31.557 85.338 16 69.78 16 50.663c0-19.102 15.557-34.661 34.67-34.661 19.115 0 34.657 15.559 34.657 34.675 0 19.102-15.557 34.661-34.656 34.661"/></svg>
|
After Width: | Height: | Size: 596 B |
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M70.66 4.328l14.01 29.693c1.088 2.29 3.177 3.882 5.603 4.25l31.347 4.76c6.087.926 8.528 8.756 4.117 13.247L103.05 79.395c-1.75 1.78-2.544 4.352-2.132 6.867l5.352 32.641c1.043 6.337-5.33 11.182-10.778 8.19l-28.039-15.409a7.13 7.13 0 00-6.91 0l-28.039 15.41c-5.448 2.99-11.821-1.854-10.777-8.19l5.352-32.642c.415-2.515-.387-5.088-2.136-6.867L2.264 56.278C-2.146 51.787.286 43.957 6.38 43.031l31.343-4.76c2.419-.368 4.51-1.96 5.595-4.25L57.334 4.328c2.728-5.77 10.605-5.77 13.325 0z"/></svg>
|
After Width: | Height: | Size: 562 B |
@ -0,0 +1 @@
|
||||
<svg width="130" height="130" xmlns="http://www.w3.org/2000/svg"><path d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z" stroke="#979797"/></svg>
|
After Width: | Height: | Size: 440 B |
@ -0,0 +1 @@
|
||||
<svg width="128" height="110" xmlns="http://www.w3.org/2000/svg"><path d="M86.635 33.334c1.467 0 2.917.113 4.358.283C87.078 14.392 67.58.111 45.321.111 20.44.111.055 17.987.055 40.687c0 13.104 6.781 23.863 18.115 32.209l-4.527 14.352 15.82-8.364c5.666 1.182 10.207 2.395 15.858 2.395 1.42 0 2.829-.073 4.227-.189-.886-3.19-1.398-6.53-1.398-9.996 0-20.845 16.98-37.76 38.485-37.76zm-24.34-12.936c3.407 0 5.665 2.363 5.665 5.954 0 3.576-2.258 5.97-5.666 5.97-3.392 0-6.795-2.395-6.795-5.97 0-3.591 3.403-5.954 6.795-5.954zM30.616 32.323c-3.393 0-6.818-2.395-6.818-5.971 0-3.591 3.425-5.954 6.818-5.954 3.392 0 5.65 2.363 5.65 5.954 0 3.576-2.258 5.97-5.65 5.97z"/><path d="M127.945 70.52c0-19.075-18.108-34.623-38.448-34.623-21.537 0-38.5 15.548-38.5 34.623 0 19.108 16.963 34.622 38.5 34.622 4.508 0 9.058-1.2 13.584-2.395l12.414 7.167-3.404-11.923c9.087-7.184 15.854-16.712 15.854-27.471zm-50.928-5.97c-2.254 0-4.53-2.362-4.53-4.773 0-2.378 2.276-4.771 4.53-4.771 3.422 0 5.665 2.393 5.665 4.771 0 2.41-2.243 4.773-5.665 4.773zm24.897 0c-2.24 0-4.498-2.362-4.498-4.773 0-2.378 2.258-4.771 4.498-4.771 3.392 0 5.665 2.393 5.665 4.771 0 2.41-2.273 4.773-5.665 4.773z"/></svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,22 @@
|
||||
# replace default config
|
||||
|
||||
# multipass: true
|
||||
# full: true
|
||||
|
||||
plugins:
|
||||
|
||||
# - name
|
||||
#
|
||||
# or:
|
||||
# - name: false
|
||||
# - name: true
|
||||
#
|
||||
# or:
|
||||
# - name:
|
||||
# param1: 1
|
||||
# param2: 2
|
||||
|
||||
- removeAttrs:
|
||||
attrs:
|
||||
- 'fill'
|
||||
- 'fill-rule'
|
@ -0,0 +1,42 @@
|
||||
import Vue from 'vue'
|
||||
// A modern alternative to CSS resets
|
||||
import './styles/reset.css'
|
||||
// https://github.com/necolas/normalize.css
|
||||
import 'normalize.css/normalize.css'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
|
||||
import './icons'
|
||||
import './permission'
|
||||
|
||||
// vant
|
||||
import {
|
||||
Button,
|
||||
Toast,
|
||||
Dialog,
|
||||
Field,
|
||||
Form,
|
||||
Notify,
|
||||
Cell,
|
||||
CellGroup,
|
||||
NavBar
|
||||
} from 'vant'
|
||||
Vue.use(Toast)
|
||||
.use(Dialog)
|
||||
.use(Notify)
|
||||
.use(Field)
|
||||
.use(Form)
|
||||
.use(Button)
|
||||
.use(Cell)
|
||||
.use(CellGroup)
|
||||
.use(NavBar)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
@ -0,0 +1,29 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '*',
|
||||
name: 'Home',
|
||||
component: () => import('@/views/home')
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: () => import('@/views/auth/login')
|
||||
},
|
||||
{
|
||||
path: '/iconfont',
|
||||
name: 'IconFont',
|
||||
component: () => import('@/views/about/iconfont')
|
||||
}
|
||||
]
|
||||
|
||||
const router = new VueRouter({
|
||||
scrollBehavior: () => ({ y: 0 }),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
@ -0,0 +1,7 @@
|
||||
const getters = {
|
||||
avatar: state => state.user.avatar,
|
||||
name: state => state.user.name,
|
||||
token: state => state.user.token
|
||||
}
|
||||
|
||||
export default getters
|
@ -0,0 +1,22 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import getters from './getters'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
// https://webpack.js.org/guides/dependency-management/#requirecontext
|
||||
const modulesFiles = require.context('./modules', true, /\.js$/)
|
||||
|
||||
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
|
||||
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
|
||||
const value = modulesFiles(modulePath)
|
||||
modules[moduleName] = value.default
|
||||
return modules
|
||||
}, {})
|
||||
|
||||
const store = new Vuex.Store({
|
||||
modules,
|
||||
getters
|
||||
})
|
||||
|
||||
export default store
|
@ -0,0 +1,90 @@
|
||||
import { login, logout, getInfo } from '@/api/user'
|
||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||
|
||||
const state = {
|
||||
token: getToken(),
|
||||
name: '',
|
||||
avatar: ''
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_TOKEN: (state, token) => {
|
||||
state.token = token
|
||||
},
|
||||
SET_NAME: (state, name) => {
|
||||
state.name = name
|
||||
},
|
||||
SET_AVATAR: (state, avatar) => {
|
||||
state.avatar = avatar
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
// 登录
|
||||
login({ commit }, userInfo) {
|
||||
const { phonenum, password } = userInfo
|
||||
return new Promise((resolve, reject) => {
|
||||
login({ phonenum, password })
|
||||
.then(res => {
|
||||
const { token } = res.entry
|
||||
commit('SET_TOKEN', token)
|
||||
setToken(token)
|
||||
resolve()
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 登出
|
||||
logout({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout(state.token)
|
||||
.then(() => {
|
||||
commit('SET_TOKEN', '')
|
||||
removeToken()
|
||||
|
||||
resolve()
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取基本用户信息
|
||||
getInfo({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getInfo(state.token)
|
||||
.then(res => {
|
||||
const data = res.entry
|
||||
if (!data) {
|
||||
reject(new Error('获取基本信息失败,请重新登录'))
|
||||
}
|
||||
commit('SET_NAME', data.name)
|
||||
commit('SET_AVATAR', data.avatar)
|
||||
resolve(data)
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 重置token
|
||||
resetToken({ commit }) {
|
||||
return new Promise(resolve => {
|
||||
commit('SET_TOKEN', '')
|
||||
removeToken()
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v2.0 | 20110126
|
||||
License: none (public domain)
|
||||
*/
|
||||
|
||||
html,
|
||||
body,
|
||||
div,
|
||||
span,
|
||||
applet,
|
||||
object,
|
||||
iframe,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
blockquote,
|
||||
pre,
|
||||
a,
|
||||
abbr,
|
||||
acronym,
|
||||
address,
|
||||
big,
|
||||
cite,
|
||||
code,
|
||||
del,
|
||||
dfn,
|
||||
em,
|
||||
img,
|
||||
ins,
|
||||
kbd,
|
||||
q,
|
||||
s,
|
||||
samp,
|
||||
small,
|
||||
strike,
|
||||
strong,
|
||||
sub,
|
||||
sup,
|
||||
tt,
|
||||
var,
|
||||
b,
|
||||
u,
|
||||
i,
|
||||
center,
|
||||
dl,
|
||||
dt,
|
||||
dd,
|
||||
ol,
|
||||
ul,
|
||||
li,
|
||||
fieldset,
|
||||
form,
|
||||
label,
|
||||
legend,
|
||||
table,
|
||||
caption,
|
||||
tbody,
|
||||
tfoot,
|
||||
thead,
|
||||
tr,
|
||||
th,
|
||||
td,
|
||||
article,
|
||||
aside,
|
||||
canvas,
|
||||
details,
|
||||
embed,
|
||||
figure,
|
||||
figcaption,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
output,
|
||||
ruby,
|
||||
section,
|
||||
summary,
|
||||
time,
|
||||
mark,
|
||||
audio,
|
||||
video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
ol,
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
blockquote,
|
||||
q {
|
||||
quotes: none;
|
||||
}
|
||||
blockquote:before,
|
||||
blockquote:after,
|
||||
q:before,
|
||||
q:after {
|
||||
content: "";
|
||||
content: none;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
const key = 'MallToken'
|
||||
|
||||
export function getToken() {
|
||||
return Cookies.get(key)
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
return Cookies.set(key, token)
|
||||
}
|
||||
|
||||
export function removeToken() {
|
||||
return Cookies.remove(key)
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
import axios from 'axios'
|
||||
import { Toast, Dialog } from 'vant'
|
||||
import store from '@/store'
|
||||
import { getToken } from '@/utils/auth'
|
||||
|
||||
// 创建一个axios实例
|
||||
const service = axios.create({
|
||||
baseURL: process.env.VUE_APP_BASE_API,
|
||||
// withCredentials: true,
|
||||
timeout: 5000
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
config => {
|
||||
if (store.getters.token) {
|
||||
// ['X-Token']是我这里自定义测试而塞到请求头中
|
||||
config.headers['X-TOKEN'] = getToken()
|
||||
}
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
console.log(error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(
|
||||
response => {
|
||||
const res = response.data
|
||||
|
||||
// 与后端约定的错误码
|
||||
if (res.code !== 200) {
|
||||
Toast.fail(res.message)
|
||||
// 现约定 50001:无效token 50002:token过期
|
||||
if (res.code === 50001 || res.code === 50002) {
|
||||
Dialog.alert({
|
||||
title: '提示',
|
||||
message: '您还未登录或登录已过期,请重新登录'
|
||||
}).then(() => {
|
||||
store.dispatch('user/resetToken').then(() => {
|
||||
location.reload()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(res.message || 'Error'))
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log('err' + error)
|
||||
Toast.fail(error.message)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="icon-font">
|
||||
<van-nav-bar title="svg图标" left-text="返回" left-arrow @click-left="onClickLeft"/>
|
||||
|
||||
<svg-icon icon-class="backtop" :width="30" :height="30" />
|
||||
<svg-icon icon-class="wechat" :width="30" :height="30" />
|
||||
<span style="color:red">
|
||||
<svg-icon icon-class="qq" :width="30" :height="30" />
|
||||
</span>
|
||||
<span fill="red">
|
||||
<svg-icon icon-class="search" :width="30" :height="30" />
|
||||
</span>
|
||||
<svg-icon icon-class="user" :width="30" :height="30" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'IconFont',
|
||||
methods: {
|
||||
onClickLeft() {
|
||||
this.$router.go(-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<div class="login-container">
|
||||
<div class="header">
|
||||
<img class="header__logo" src="@/assets/logo.png" alt="logo" />
|
||||
<p class="header__title">panda-vue-template</p>
|
||||
</div>
|
||||
|
||||
<van-form class="form" @submit="onSubmit">
|
||||
<van-field
|
||||
v-model="form.phonenum"
|
||||
type="number"
|
||||
required
|
||||
clearable
|
||||
name="手机号"
|
||||
label="手机号"
|
||||
placeholder="请输入手机号"
|
||||
:rules="[{validator:checkPhoneNum, required: true, message:'请填写正确的手机号!'}]"
|
||||
/>
|
||||
|
||||
<van-field
|
||||
v-model="form.password"
|
||||
type="number"
|
||||
required
|
||||
center
|
||||
clearable
|
||||
name="手机号"
|
||||
label="验证码"
|
||||
placeholder="请输入短信验证码"
|
||||
:rules="[{ required: true, message: '请填写正确的验证码!' }]"
|
||||
>
|
||||
<van-button
|
||||
slot="button"
|
||||
size="small"
|
||||
plain
|
||||
type="info"
|
||||
native-type="button"
|
||||
@click.stop="form.password = 1234"
|
||||
>发送验证码</van-button>
|
||||
</van-field>
|
||||
|
||||
<div style="margin: 36px;">
|
||||
<van-button
|
||||
round
|
||||
block
|
||||
:loading="loading"
|
||||
type="info"
|
||||
loading-text="登录中..."
|
||||
native-type="submit"
|
||||
>提交</van-button>
|
||||
</div>
|
||||
</van-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Login',
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
phonenum: 123456789,
|
||||
password: 1234
|
||||
},
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route: {
|
||||
handler(route) {
|
||||
console.log('route:', route)
|
||||
this.redirect = (route.query && route.query.redirect) || '/'
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 校检手机号
|
||||
checkPhoneNum(num) {
|
||||
if (num === 123456789) return true
|
||||
const reg = /^[1][3,4,5,7,8][0-9]{9}$/
|
||||
if (reg.test(num)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
// 提交
|
||||
onSubmit() {
|
||||
this.loading = true
|
||||
this.$store
|
||||
.dispatch('user/login', this.form)
|
||||
.then(() => {
|
||||
this.$notify({
|
||||
type: 'success',
|
||||
message: '登录成功',
|
||||
duration: 2000,
|
||||
onOpened: () => {
|
||||
location.href = this.redirect
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-container {
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.header__logo {
|
||||
display: block;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
padding-top: 80px;
|
||||
}
|
||||
.header__title {
|
||||
font-size: 36px;
|
||||
color: #000;
|
||||
font-weight: normal;
|
||||
padding-top: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.form {
|
||||
// background: red;
|
||||
padding: 24px;
|
||||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<img class="logo" src="@/assets/logo.png" alt="logo" />
|
||||
|
||||
<van-cell-group title="相关介绍:">
|
||||
<van-cell title="vue-mall" is-link url="https://github.com/Ewall1106/mall" />
|
||||
<van-cell
|
||||
title="panda-vue-template"
|
||||
is-link
|
||||
url="https://github.com/Ewall1106/panda-vue-template"
|
||||
/>
|
||||
<van-cell title="yapi接口文档" is-link url="http://yapi.demo.qunar.com/project/17982" />
|
||||
</van-cell-group>
|
||||
|
||||
<van-cell-group title="功能介绍:">
|
||||
<van-cell
|
||||
title="一次讲清移动端适配解决方案—rem和vw"
|
||||
is-link
|
||||
url="https://github.com/Ewall1106/panda-vue-template"
|
||||
/>
|
||||
<van-cell title="vue-cli3.0与vant的引入" is-link url="https://github.com/Ewall1106/mall" />
|
||||
<van-cell title="axios的封装与数据mock" is-link url="https://github.com/Ewall1106/mall" />
|
||||
<van-cell title="vuex的基本使用" is-link url="https://github.com/Ewall1106/mall" />
|
||||
</van-cell-group>
|
||||
|
||||
<van-cell-group title="组件补充:">
|
||||
<van-cell title="svg图标" is-link to="IconFont" />
|
||||
<!-- <van-cell title="返回顶部" is-link to="About" target="_blank" /> -->
|
||||
</van-cell-group>
|
||||
<back-top />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BackTop from '@/components/BackTop'
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
components: {
|
||||
BackTop
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
val: '11111'
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.home {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f593;
|
||||
.logo {
|
||||
display: block;
|
||||
width: 200px;
|
||||
margin: 0 auto;
|
||||
padding-top: 40px;
|
||||
}
|
||||
h2 {
|
||||
padding: 10px 0;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,103 @@
|
||||
'use strict'
|
||||
const path = require('path')
|
||||
|
||||
const mockUrl = 'http://yapi.demo.qunar.com/mock/17982'
|
||||
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, dir)
|
||||
}
|
||||
|
||||
// All configuration item explanations can be find in https://cli.vuejs.org/config/
|
||||
module.exports = {
|
||||
publicPath: '/',
|
||||
outputDir: 'dist',
|
||||
assetsDir: 'static',
|
||||
lintOnSave: process.env.NODE_ENV === 'development',
|
||||
productionSourceMap: false,
|
||||
devServer: {
|
||||
port: 8080,
|
||||
open: true,
|
||||
overlay: {
|
||||
warnings: false,
|
||||
errors: true
|
||||
},
|
||||
proxy: {
|
||||
'/dev-api': {
|
||||
target: mockUrl,
|
||||
pathRewrite: { '^/dev-api': '' },
|
||||
secure: false,
|
||||
changeOrigin: true
|
||||
}
|
||||
}
|
||||
},
|
||||
configureWebpack: {
|
||||
// provide the app's title in webpack's name field, so that
|
||||
// it can be accessed in index.html to inject the correct title.
|
||||
name: 'panda vue template',
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve('src')
|
||||
}
|
||||
}
|
||||
},
|
||||
chainWebpack (config) {
|
||||
config.plugins.delete('preload') // TODO: need test
|
||||
config.plugins.delete('prefetch') // TODO: need test
|
||||
|
||||
// set svg-sprite-loader
|
||||
config.module
|
||||
.rule('svg')
|
||||
.exclude.add(resolve('src/icons'))
|
||||
.end()
|
||||
config.module
|
||||
.rule('icons')
|
||||
.test(/\.svg$/)
|
||||
.include.add(resolve('src/icons'))
|
||||
.end()
|
||||
.use('svg-sprite-loader')
|
||||
.loader('svg-sprite-loader')
|
||||
.options({
|
||||
symbolId: 'icon-[name]'
|
||||
})
|
||||
.end()
|
||||
|
||||
// set preserveWhitespace
|
||||
config.module
|
||||
.rule('vue')
|
||||
.use('vue-loader')
|
||||
.loader('vue-loader')
|
||||
.tap(options => {
|
||||
options.compilerOptions.preserveWhitespace = true
|
||||
return options
|
||||
})
|
||||
.end()
|
||||
|
||||
config
|
||||
// https://webpack.js.org/configuration/devtool/#development
|
||||
.when(process.env.NODE_ENV === 'development', config =>
|
||||
config.devtool('cheap-source-map')
|
||||
)
|
||||
|
||||
config.when(process.env.NODE_ENV !== 'development', config => {
|
||||
config.optimization.splitChunks({
|
||||
chunks: 'all',
|
||||
cacheGroups: {
|
||||
libs: {
|
||||
name: 'chunk-libs',
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
priority: 10,
|
||||
chunks: 'initial' // only package third parties that are initially dependent
|
||||
},
|
||||
commons: {
|
||||
name: 'chunk-commons',
|
||||
test: resolve('src/components'), // can customize your rules
|
||||
minChunks: 3, // minimum common number
|
||||
priority: 5,
|
||||
reuseExistingChunk: true
|
||||
}
|
||||
}
|
||||
})
|
||||
config.optimization.runtimeChunk('single')
|
||||
})
|
||||
}
|
||||
}
|