Pre Merge pull request !2 from Xydawn/master
commit
d984e6c73b
|
@ -1,5 +0,0 @@
|
|||
# just a flag
|
||||
ENV = 'development'
|
||||
|
||||
# base api
|
||||
VUE_APP_BASE_API = '/dev-api'
|
|
@ -1,6 +0,0 @@
|
|||
# just a flag
|
||||
ENV = 'production'
|
||||
|
||||
# base api
|
||||
VUE_APP_BASE_API = '/prod-api'
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
NODE_ENV = production
|
||||
|
||||
# just a flag
|
||||
ENV = 'staging'
|
||||
|
||||
# base api
|
||||
VUE_APP_BASE_API = '/stage-api'
|
||||
|
|
@ -22,7 +22,7 @@ module.exports = {
|
|||
}
|
||||
}],
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"vue/multiline-html-element-content-newline":"off",
|
||||
"vue/multiline-html-element-content-newline": "off",
|
||||
"vue/name-property-casing": ["error", "PascalCase"],
|
||||
"vue/no-v-html": "off",
|
||||
'accessor-pairs': 2,
|
||||
|
@ -47,7 +47,7 @@ module.exports = {
|
|||
'curly': [2, 'multi-line'],
|
||||
'dot-location': [2, 'property'],
|
||||
'eol-last': 2,
|
||||
'eqeqeq': ["error", "always", {"null": "ignore"}],
|
||||
'eqeqeq': ["error", "always", { "null": "ignore" }],
|
||||
'generator-star-spacing': [2, {
|
||||
'before': true,
|
||||
'after': true
|
||||
|
|
|
@ -14,3 +14,5 @@ tests/**/coverage/
|
|||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
.pnpm-debug.log
|
||||
pnpm-lock.yaml
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
const { run } = require('runjs')
|
||||
const chalk = require('chalk')
|
||||
const config = require('../vue.config.js')
|
||||
const rawArgv = process.argv.slice(2)
|
||||
const args = rawArgv.join(' ')
|
||||
|
||||
if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
|
||||
const report = rawArgv.includes('--report')
|
||||
|
||||
run(`vue-cli-service build ${args}`)
|
||||
|
||||
const port = 9526
|
||||
const publicPath = config.publicPath
|
||||
|
||||
var connect = require('connect')
|
||||
var serveStatic = require('serve-static')
|
||||
const app = connect()
|
||||
|
||||
app.use(
|
||||
publicPath,
|
||||
serveStatic('./dist', {
|
||||
index: ['index.html', '/']
|
||||
})
|
||||
)
|
||||
|
||||
app.listen(port, function () {
|
||||
console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`))
|
||||
if (report) {
|
||||
console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`))
|
||||
}
|
||||
|
||||
})
|
||||
} else {
|
||||
run(`vue-cli-service build ${args}`)
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<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="/favicon.ico">
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<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>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
|
@ -1,8 +1,8 @@
|
|||
const Mock = require('mockjs')
|
||||
const { param2Obj } = require('./utils')
|
||||
import Mock from 'mockjs'
|
||||
import utils from './utils'
|
||||
|
||||
const user = require('./user')
|
||||
const table = require('./table')
|
||||
import user from './user'
|
||||
import table from './table'
|
||||
|
||||
const mocks = [
|
||||
...user,
|
||||
|
@ -36,7 +36,7 @@ function mockXHR() {
|
|||
result = respond({
|
||||
method: type,
|
||||
body: JSON.parse(body),
|
||||
query: param2Obj(url)
|
||||
query: utils.param2Obj(url)
|
||||
})
|
||||
} else {
|
||||
result = respond
|
||||
|
@ -50,7 +50,7 @@ function mockXHR() {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
mocks,
|
||||
mockXHR
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ function unregisterRoutes() {
|
|||
// for mock server
|
||||
const responseFake = (url, type, respond) => {
|
||||
return {
|
||||
url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`),
|
||||
url: new RegExp(`${import.meta.env.VUE_APP_BASE_API}${url}`),
|
||||
type: type || 'get',
|
||||
response(req, res) {
|
||||
console.log('request invoke:' + req.path)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const Mock = require('mockjs')
|
||||
import Mock from 'mockjs'
|
||||
|
||||
const data = Mock.mock({
|
||||
'items|30': [{
|
||||
|
@ -11,7 +11,7 @@ const data = Mock.mock({
|
|||
}]
|
||||
})
|
||||
|
||||
module.exports = [
|
||||
export default [
|
||||
{
|
||||
url: '/vue-admin-template/table/list',
|
||||
type: 'get',
|
||||
|
|
|
@ -23,7 +23,7 @@ const users = {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
export default [
|
||||
// user login
|
||||
{
|
||||
url: '/vue-admin-template/user/login',
|
||||
|
|
|
@ -20,6 +20,6 @@ function param2Obj(url) {
|
|||
return obj
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
param2Obj
|
||||
}
|
||||
|
|
75
package.json
75
package.json
|
@ -1,54 +1,41 @@
|
|||
{
|
||||
"name": "vue-admin-template",
|
||||
"version": "4.4.0",
|
||||
"description": "A vue admin template with Element UI & axios & iconfont & permission control & lint",
|
||||
"author": "Pan <panfree23@gmail.com>",
|
||||
"description": "A vue admin template with Element UI & axios & vite & permission control & lint",
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve",
|
||||
"build:prod": "vue-cli-service build",
|
||||
"build:stage": "vue-cli-service build --mode staging",
|
||||
"preview": "node build/index.js --preview",
|
||||
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
|
||||
"lint": "eslint --ext .js,.vue src",
|
||||
"test:unit": "jest --clearCache && vue-cli-service test:unit",
|
||||
"test:ci": "npm run lint && npm run test:unit"
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint --ext .js,.vue src"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "0.18.1",
|
||||
"core-js": "3.6.5",
|
||||
"element-ui": "2.13.2",
|
||||
"js-cookie": "2.2.0",
|
||||
"normalize.css": "7.0.0",
|
||||
"nprogress": "0.2.0",
|
||||
"path-to-regexp": "2.4.0",
|
||||
"vue": "2.6.10",
|
||||
"vue-router": "3.0.6",
|
||||
"vuex": "3.1.0"
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.6.5",
|
||||
"element-ui": "^2.13.2",
|
||||
"js-cookie": "^2.2.0",
|
||||
"normalize.css": "^7.0.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"path-to-regexp": "^2.4.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-router": "^3.0.6",
|
||||
"vuex": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "4.4.4",
|
||||
"@vue/cli-plugin-eslint": "4.4.4",
|
||||
"@vue/cli-plugin-unit-jest": "4.4.4",
|
||||
"@vue/cli-service": "4.4.4",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"autoprefixer": "9.5.1",
|
||||
"babel-eslint": "10.1.0",
|
||||
"babel-jest": "23.6.0",
|
||||
"babel-plugin-dynamic-import-node": "2.3.3",
|
||||
"chalk": "2.4.2",
|
||||
"connect": "3.6.6",
|
||||
"eslint": "6.7.2",
|
||||
"eslint-plugin-vue": "6.2.2",
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"mockjs": "1.0.1-beta3",
|
||||
"runjs": "4.3.2",
|
||||
"sass": "1.26.8",
|
||||
"sass-loader": "8.0.2",
|
||||
"script-ext-html-webpack-plugin": "2.1.3",
|
||||
"serve-static": "1.13.2",
|
||||
"svg-sprite-loader": "4.1.3",
|
||||
"svgo": "1.2.2",
|
||||
"vue-template-compiler": "2.6.10"
|
||||
"@vue/compiler-sfc": "^3.2.26",
|
||||
"autoprefixer": "^9.5.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"chalk": "^2.4.2",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"mockjs": "^1.0.1-beta3",
|
||||
"runjs": "^4.3.2",
|
||||
"sass": "^1.26.8",
|
||||
"serve-static": "^1.13.2",
|
||||
"svg-sprite-loader": "^6.0.11",
|
||||
"vite": "^2.7.10",
|
||||
"vite-plugin-vue2": "^1.9.2",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
@ -59,4 +46,4 @@
|
|||
"npm": ">= 3.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
|||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<title><%= webpackConfig.name %></title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
import Vue from 'vue'
|
||||
import SvgIcon from '@/components/SvgIcon'// svg component
|
||||
import SvgIcon from '@/components/SvgIcon' // svg component
|
||||
|
||||
// register globally
|
||||
Vue.component('svg-icon', SvgIcon)
|
||||
|
||||
const req = require.context('./svg', false, /\.svg$/)
|
||||
const requireAll = requireContext => requireContext.keys().map(requireContext)
|
||||
requireAll(req)
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<div class="right-menu">
|
||||
<el-dropdown class="avatar-container" trigger="click">
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">
|
||||
<el-avatar shape="square" size="medium" :src="avatar+'?imageView2/1/w/80/h/80'" />
|
||||
<!-- <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar"> -->
|
||||
<i class="el-icon-caret-bottom" />
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown" class="user-dropdown">
|
||||
|
@ -118,12 +119,12 @@ export default {
|
|||
margin-top: 5px;
|
||||
position: relative;
|
||||
|
||||
.user-avatar {
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
// .user-avatar {
|
||||
// cursor: pointer;
|
||||
// width: 40px;
|
||||
// height: 40px;
|
||||
// border-radius: 10px;
|
||||
// }
|
||||
|
||||
.el-icon-caret-bottom {
|
||||
cursor: pointer;
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
<template>
|
||||
<span>
|
||||
<template v-if="icon.includes('el-icon')">
|
||||
<i :class="[icon, 'sub-el-icon']" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<svg-icon :icon-class="icon" />
|
||||
</template>
|
||||
<template v-if="title">
|
||||
<span slot="title" style="padding-left: 5px;">{{ title }}</span>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'MenuItem',
|
||||
functional: true,
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
|
@ -11,24 +23,24 @@ export default {
|
|||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
render(h, context) {
|
||||
const { icon, title } = context.props
|
||||
const vnodes = []
|
||||
|
||||
if (icon) {
|
||||
if (icon.includes('el-icon')) {
|
||||
vnodes.push(<i class={[icon, 'sub-el-icon']} />)
|
||||
} else {
|
||||
vnodes.push(<svg-icon icon-class={icon}/>)
|
||||
}
|
||||
}
|
||||
|
||||
if (title) {
|
||||
vnodes.push(<span slot='title'>{(title)}</span>)
|
||||
}
|
||||
return vnodes
|
||||
}
|
||||
/* render(h, context) {
|
||||
const { icon, title } = context.props
|
||||
const vnodes = []
|
||||
|
||||
if (icon) {
|
||||
if (icon.includes('el-icon')) {
|
||||
vnodes.push(<i class={[icon, 'sub-el-icon']} />)
|
||||
} else {
|
||||
vnodes.push(<svg-icon icon-class={icon} />)
|
||||
}
|
||||
}
|
||||
|
||||
if (title) {
|
||||
vnodes.push(<span slot='title'>{(title)}</span>)
|
||||
}
|
||||
return vnodes
|
||||
} */
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import path from 'path'
|
||||
import path from 'path-browserify '
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import Item from './Item'
|
||||
import AppLink from './Link'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div :class="{'has-logo':showLogo}">
|
||||
<div :class="{ 'has-logo': showLogo }">
|
||||
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||
<el-menu
|
||||
|
@ -12,7 +12,12 @@
|
|||
:collapse-transition="false"
|
||||
mode="vertical"
|
||||
>
|
||||
<sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />
|
||||
<sidebar-item
|
||||
v-for="route in routes"
|
||||
:key="route.path"
|
||||
:item="route"
|
||||
:base-path="route.path"
|
||||
/>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
@ -22,7 +27,6 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
import Logo from './Logo'
|
||||
import SidebarItem from './SidebarItem'
|
||||
import variables from '@/styles/variables.scss'
|
||||
|
||||
export default {
|
||||
components: { SidebarItem, Logo },
|
||||
|
@ -46,7 +50,10 @@ export default {
|
|||
return this.$store.state.settings.sidebarLogo
|
||||
},
|
||||
variables() {
|
||||
return variables
|
||||
return {
|
||||
menuBg: '#304156',
|
||||
menuText: 'white'
|
||||
}
|
||||
},
|
||||
isCollapse() {
|
||||
return !this.sidebar.opened
|
||||
|
|
|
@ -51,8 +51,8 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/styles/mixin.scss";
|
||||
@import "~@/styles/variables.scss";
|
||||
@import "@/styles/mixin.scss";
|
||||
@import "@/styles/variables.scss";
|
||||
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Vue from 'vue'
|
||||
|
||||
import 'normalize.css/normalize.css' // A modern alternative to CSS resets
|
||||
|
||||
import ElementUI from 'element-ui'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
import locale from 'element-ui/lib/locale/lang/en' // lang i18n
|
||||
|
@ -13,6 +12,7 @@ import store from './store'
|
|||
import router from './router'
|
||||
|
||||
import '@/icons' // icon
|
||||
import mock from '../mock'
|
||||
import '@/permission' // permission control
|
||||
|
||||
/**
|
||||
|
@ -23,9 +23,9 @@ import '@/permission' // permission control
|
|||
* Currently MockJs will be used in the production environment,
|
||||
* please remove it before going online ! ! !
|
||||
*/
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
const { mockXHR } = require('../mock')
|
||||
mockXHR()
|
||||
|
||||
if (import.meta.env.MODE !== 'production') {
|
||||
mock.mockXHR()
|
||||
}
|
||||
|
||||
// set ElementUI lang to EN
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
import { readFileSync, readdirSync } from 'fs'
|
||||
|
||||
let idPerfix = ''
|
||||
const svgTitle = /<svg([^>+].*?)>/
|
||||
const clearHeightWidth = /(width|height)="([^>+].*?)"/g
|
||||
|
||||
const hasViewBox = /(viewBox="[^>+].*?")/g
|
||||
|
||||
const clearReturn = /(\r)|(\n)/g
|
||||
|
||||
function findSvgFile(dir) {
|
||||
const svgRes = []
|
||||
const dirents = readdirSync(dir, {
|
||||
withFileTypes: true
|
||||
})
|
||||
for (const dirent of dirents) {
|
||||
if (dirent.isDirectory()) {
|
||||
svgRes.push(...findSvgFile(dir + dirent.name + '/'))
|
||||
} else {
|
||||
const svg = readFileSync(dir + dirent.name)
|
||||
.toString()
|
||||
.replace(clearReturn, '')
|
||||
.replace(svgTitle, ($1, $2) => {
|
||||
// console.log(++i)
|
||||
// console.log(dirent.name)
|
||||
let width = 0
|
||||
let height = 0
|
||||
let content = $2.replace(
|
||||
clearHeightWidth,
|
||||
(s1, s2, s3) => {
|
||||
if (s2 === 'width') {
|
||||
width = s3
|
||||
} else if (s2 === 'height') {
|
||||
height = s3
|
||||
}
|
||||
return ''
|
||||
}
|
||||
)
|
||||
if (!hasViewBox.test($2)) {
|
||||
content += `viewBox="0 0 ${width} ${height}"`
|
||||
}
|
||||
return `<symbol id="${idPerfix}-${dirent.name.replace(
|
||||
'.svg',
|
||||
''
|
||||
)}" ${content}>`
|
||||
})
|
||||
.replace('</svg>', '</symbol>')
|
||||
svgRes.push(svg)
|
||||
}
|
||||
}
|
||||
return svgRes
|
||||
}
|
||||
|
||||
export const svgBuilder = (path, perfix = 'icon') => {
|
||||
if (path === '') return
|
||||
idPerfix = perfix
|
||||
const res = findSvgFile(path)
|
||||
// console.log(res.length)
|
||||
// const res = []
|
||||
return {
|
||||
name: 'svg-transform',
|
||||
transformIndexHtml(html) {
|
||||
return html.replace(
|
||||
'<body>',
|
||||
`
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
|
||||
${res.join('')}
|
||||
</svg>
|
||||
`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = {
|
||||
export default {
|
||||
|
||||
title: 'Vue Admin Template',
|
||||
|
||||
|
@ -12,5 +12,5 @@ module.exports = {
|
|||
* @type {boolean} true | false
|
||||
* @description Whether show the logo in sidebar
|
||||
*/
|
||||
sidebarLogo: false
|
||||
sidebarLogo: true
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#app {
|
||||
|
||||
.main-container {
|
||||
min-height: 100%;
|
||||
transition: margin-left .28s;
|
||||
transition: margin-left 0.28s;
|
||||
margin-left: $sideBarWidth;
|
||||
position: relative;
|
||||
}
|
||||
|
@ -22,7 +21,8 @@
|
|||
|
||||
// reset element-ui css
|
||||
.horizontal-collapse-transition {
|
||||
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
|
||||
transition: 0s width ease-in-out, 0s padding-left ease-in-out,
|
||||
0s padding-right ease-in-out;
|
||||
}
|
||||
|
||||
.scrollbar-wrapper {
|
||||
|
@ -76,11 +76,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.is-active>.el-submenu__title {
|
||||
.is-active > .el-submenu__title {
|
||||
color: $subMenuActiveText !important;
|
||||
}
|
||||
|
||||
& .nest-menu .el-submenu>.el-submenu__title,
|
||||
& .nest-menu .el-submenu > .el-submenu__title,
|
||||
& .el-submenu .el-menu-item {
|
||||
min-width: $sideBarWidth !important;
|
||||
background-color: $subMenuBg !important;
|
||||
|
@ -100,7 +100,7 @@
|
|||
margin-left: 54px;
|
||||
}
|
||||
|
||||
.submenu-title-noDropdown {
|
||||
& > .submenu-title-noDropdown {
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
|
||||
|
@ -117,10 +117,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.el-submenu {
|
||||
& > .el-submenu {
|
||||
overflow: hidden;
|
||||
|
||||
&>.el-submenu__title {
|
||||
& > .el-submenu__title {
|
||||
padding: 0 !important;
|
||||
|
||||
.svg-icon {
|
||||
|
@ -137,10 +137,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse {
|
||||
& > .el-menu--collapse {
|
||||
.el-submenu {
|
||||
&>.el-submenu__title {
|
||||
&>span {
|
||||
& > .el-submenu__title {
|
||||
& > span {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
|
@ -163,7 +163,7 @@
|
|||
}
|
||||
|
||||
.sidebar-container {
|
||||
transition: transform .28s;
|
||||
transition: transform 0.28s;
|
||||
width: $sideBarWidth !important;
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,6 @@
|
|||
}
|
||||
|
||||
.withoutAnimation {
|
||||
|
||||
.main-container,
|
||||
.sidebar-container {
|
||||
transition: none;
|
||||
|
@ -187,7 +186,7 @@
|
|||
|
||||
// when menu collapsed
|
||||
.el-menu--vertical {
|
||||
&>.el-menu {
|
||||
& > .el-menu {
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
@ -197,7 +196,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.nest-menu .el-submenu>.el-submenu__title,
|
||||
.nest-menu .el-submenu > .el-submenu__title,
|
||||
.el-menu-item {
|
||||
&:hover {
|
||||
// you can use $subMenuHover
|
||||
|
@ -206,7 +205,7 @@
|
|||
}
|
||||
|
||||
// the scroll bar appears when the subMenu is too long
|
||||
>.el-menu--popup {
|
||||
> .el-menu--popup {
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { getToken } from '@/utils/auth'
|
|||
|
||||
// create an axios instance
|
||||
const service = axios.create({
|
||||
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
|
||||
baseURL: import.meta.env.VUE_APP_BASE_API, // url = base url + request url
|
||||
// withCredentials: true, // send cookies when cross-domain requests
|
||||
timeout: 5000 // request timeout
|
||||
})
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
jest: true
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
import { mount, createLocalVue } from '@vue/test-utils'
|
||||
import VueRouter from 'vue-router'
|
||||
import ElementUI from 'element-ui'
|
||||
import Breadcrumb from '@/components/Breadcrumb/index.vue'
|
||||
|
||||
const localVue = createLocalVue()
|
||||
localVue.use(VueRouter)
|
||||
localVue.use(ElementUI)
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
children: [{
|
||||
path: 'dashboard',
|
||||
name: 'dashboard'
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/menu',
|
||||
name: 'menu',
|
||||
children: [{
|
||||
path: 'menu1',
|
||||
name: 'menu1',
|
||||
meta: { title: 'menu1' },
|
||||
children: [{
|
||||
path: 'menu1-1',
|
||||
name: 'menu1-1',
|
||||
meta: { title: 'menu1-1' }
|
||||
},
|
||||
{
|
||||
path: 'menu1-2',
|
||||
name: 'menu1-2',
|
||||
redirect: 'noredirect',
|
||||
meta: { title: 'menu1-2' },
|
||||
children: [{
|
||||
path: 'menu1-2-1',
|
||||
name: 'menu1-2-1',
|
||||
meta: { title: 'menu1-2-1' }
|
||||
},
|
||||
{
|
||||
path: 'menu1-2-2',
|
||||
name: 'menu1-2-2'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
|
||||
const router = new VueRouter({
|
||||
routes
|
||||
})
|
||||
|
||||
describe('Breadcrumb.vue', () => {
|
||||
const wrapper = mount(Breadcrumb, {
|
||||
localVue,
|
||||
router
|
||||
})
|
||||
it('dashboard', () => {
|
||||
router.push('/dashboard')
|
||||
const len = wrapper.findAll('.el-breadcrumb__inner').length
|
||||
expect(len).toBe(1)
|
||||
})
|
||||
it('normal route', () => {
|
||||
router.push('/menu/menu1')
|
||||
const len = wrapper.findAll('.el-breadcrumb__inner').length
|
||||
expect(len).toBe(2)
|
||||
})
|
||||
it('nested route', () => {
|
||||
router.push('/menu/menu1/menu1-2/menu1-2-1')
|
||||
const len = wrapper.findAll('.el-breadcrumb__inner').length
|
||||
expect(len).toBe(4)
|
||||
})
|
||||
it('no meta.title', () => {
|
||||
router.push('/menu/menu1/menu1-2/menu1-2-2')
|
||||
const len = wrapper.findAll('.el-breadcrumb__inner').length
|
||||
expect(len).toBe(3)
|
||||
})
|
||||
// it('click link', () => {
|
||||
// router.push('/menu/menu1/menu1-2/menu1-2-2')
|
||||
// const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner')
|
||||
// const second = breadcrumbArray.at(1)
|
||||
// console.log(breadcrumbArray)
|
||||
// const href = second.find('a').attributes().href
|
||||
// expect(href).toBe('#/menu/menu1')
|
||||
// })
|
||||
// it('noRedirect', () => {
|
||||
// router.push('/menu/menu1/menu1-2/menu1-2-1')
|
||||
// const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner')
|
||||
// const redirectBreadcrumb = breadcrumbArray.at(2)
|
||||
// expect(redirectBreadcrumb.contains('a')).toBe(false)
|
||||
// })
|
||||
it('last breadcrumb', () => {
|
||||
router.push('/menu/menu1/menu1-2/menu1-2-1')
|
||||
const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner')
|
||||
const redirectBreadcrumb = breadcrumbArray.at(3)
|
||||
expect(redirectBreadcrumb.contains('a')).toBe(false)
|
||||
})
|
||||
})
|
|
@ -1,18 +0,0 @@
|
|||
import { shallowMount } from '@vue/test-utils'
|
||||
import Hamburger from '@/components/Hamburger/index.vue'
|
||||
describe('Hamburger.vue', () => {
|
||||
it('toggle click', () => {
|
||||
const wrapper = shallowMount(Hamburger)
|
||||
const mockFn = jest.fn()
|
||||
wrapper.vm.$on('toggleClick', mockFn)
|
||||
wrapper.find('.hamburger').trigger('click')
|
||||
expect(mockFn).toBeCalled()
|
||||
})
|
||||
it('prop isActive', () => {
|
||||
const wrapper = shallowMount(Hamburger)
|
||||
wrapper.setProps({ isActive: true })
|
||||
expect(wrapper.contains('.is-active')).toBe(true)
|
||||
wrapper.setProps({ isActive: false })
|
||||
expect(wrapper.contains('.is-active')).toBe(false)
|
||||
})
|
||||
})
|
|
@ -1,22 +0,0 @@
|
|||
import { shallowMount } from '@vue/test-utils'
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||
describe('SvgIcon.vue', () => {
|
||||
it('iconClass', () => {
|
||||
const wrapper = shallowMount(SvgIcon, {
|
||||
propsData: {
|
||||
iconClass: 'test'
|
||||
}
|
||||
})
|
||||
expect(wrapper.find('use').attributes().href).toBe('#icon-test')
|
||||
})
|
||||
it('className', () => {
|
||||
const wrapper = shallowMount(SvgIcon, {
|
||||
propsData: {
|
||||
iconClass: 'test'
|
||||
}
|
||||
})
|
||||
expect(wrapper.classes().length).toBe(1)
|
||||
wrapper.setProps({ className: 'test' })
|
||||
expect(wrapper.classes().includes('test')).toBe(true)
|
||||
})
|
||||
})
|
|
@ -1,30 +0,0 @@
|
|||
import { formatTime } from '@/utils/index.js'
|
||||
|
||||
describe('Utils:formatTime', () => {
|
||||
const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01"
|
||||
const retrofit = 5 * 1000
|
||||
|
||||
it('ten digits timestamp', () => {
|
||||
expect(formatTime((d / 1000).toFixed(0))).toBe('7月13日17时54分')
|
||||
})
|
||||
it('test now', () => {
|
||||
expect(formatTime(+new Date() - 1)).toBe('刚刚')
|
||||
})
|
||||
it('less two minute', () => {
|
||||
expect(formatTime(+new Date() - 60 * 2 * 1000 + retrofit)).toBe('2分钟前')
|
||||
})
|
||||
it('less two hour', () => {
|
||||
expect(formatTime(+new Date() - 60 * 60 * 2 * 1000 + retrofit)).toBe('2小时前')
|
||||
})
|
||||
it('less one day', () => {
|
||||
expect(formatTime(+new Date() - 60 * 60 * 24 * 1 * 1000)).toBe('1天前')
|
||||
})
|
||||
it('more than one day', () => {
|
||||
expect(formatTime(d)).toBe('7月13日17时54分')
|
||||
})
|
||||
it('format', () => {
|
||||
expect(formatTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54')
|
||||
expect(formatTime(d, '{y}-{m}-{d}')).toBe('2018-07-13')
|
||||
expect(formatTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54')
|
||||
})
|
||||
})
|
|
@ -1,14 +0,0 @@
|
|||
import { param2Obj } from '@/utils/index.js'
|
||||
describe('Utils:param2Obj', () => {
|
||||
const url = 'https://github.com/PanJiaChen/vue-element-admin?name=bill&age=29&sex=1&field=dGVzdA==&key=%E6%B5%8B%E8%AF%95'
|
||||
|
||||
it('param2Obj test', () => {
|
||||
expect(param2Obj(url)).toEqual({
|
||||
name: 'bill',
|
||||
age: '29',
|
||||
sex: '1',
|
||||
field: window.btoa('test'),
|
||||
key: '测试'
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,35 +0,0 @@
|
|||
import { parseTime } from '@/utils/index.js'
|
||||
|
||||
describe('Utils:parseTime', () => {
|
||||
const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01"
|
||||
it('timestamp', () => {
|
||||
expect(parseTime(d)).toBe('2018-07-13 17:54:01')
|
||||
})
|
||||
it('timestamp string', () => {
|
||||
expect(parseTime((d + ''))).toBe('2018-07-13 17:54:01')
|
||||
})
|
||||
it('ten digits timestamp', () => {
|
||||
expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01')
|
||||
})
|
||||
it('new Date', () => {
|
||||
expect(parseTime(new Date(d))).toBe('2018-07-13 17:54:01')
|
||||
})
|
||||
it('format', () => {
|
||||
expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54')
|
||||
expect(parseTime(d, '{y}-{m}-{d}')).toBe('2018-07-13')
|
||||
expect(parseTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54')
|
||||
})
|
||||
it('get the day of the week', () => {
|
||||
expect(parseTime(d, '{a}')).toBe('五') // 星期五
|
||||
})
|
||||
it('get the day of the week', () => {
|
||||
expect(parseTime(+d + 1000 * 60 * 60 * 24 * 2, '{a}')).toBe('日') // 星期日
|
||||
})
|
||||
it('empty argument', () => {
|
||||
expect(parseTime()).toBeNull()
|
||||
})
|
||||
|
||||
it('null', () => {
|
||||
expect(parseTime(null)).toBeNull()
|
||||
})
|
||||
})
|
|
@ -1,17 +0,0 @@
|
|||
import { validUsername, isExternal } from '@/utils/validate.js'
|
||||
|
||||
describe('Utils:validate', () => {
|
||||
it('validUsername', () => {
|
||||
expect(validUsername('admin')).toBe(true)
|
||||
expect(validUsername('editor')).toBe(true)
|
||||
expect(validUsername('xxxx')).toBe(false)
|
||||
})
|
||||
it('isExternal', () => {
|
||||
expect(isExternal('https://github.com/PanJiaChen/vue-element-admin')).toBe(true)
|
||||
expect(isExternal('http://github.com/PanJiaChen/vue-element-admin')).toBe(true)
|
||||
expect(isExternal('github.com/PanJiaChen/vue-element-admin')).toBe(false)
|
||||
expect(isExternal('/dashboard')).toBe(false)
|
||||
expect(isExternal('./dashboard')).toBe(false)
|
||||
expect(isExternal('dashboard')).toBe(false)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,25 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import { createVuePlugin } from 'vite-plugin-vue2'
|
||||
import { resolve } from 'path'
|
||||
import { svgBuilder } from './src/plugins/svgBuilder';
|
||||
// vite.config.js
|
||||
export default defineConfig({
|
||||
esbuild: {
|
||||
jsxFactory: 'h',
|
||||
jsxFragment: 'Fragment'
|
||||
},
|
||||
plugins: [
|
||||
svgBuilder('./src/icons/svg/'),
|
||||
createVuePlugin({
|
||||
jsx: true, vueTemplateOptions: { compilerOptions: { whitespace: 'condense' }},
|
||||
jsxOptions: { compositionAPI: true }
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.vue', '.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'],
|
||||
alias: {
|
||||
// vue2项目别名一般都是@,vue3中一般使用/@/, 为方便使用
|
||||
'@': resolve('src')
|
||||
}
|
||||
}
|
||||
})
|
123
vue.config.js
123
vue.config.js
|
@ -1,123 +0,0 @@
|
|||
'use strict'
|
||||
const path = require('path')
|
||||
const defaultSettings = require('./src/settings.js')
|
||||
|
||||
function resolve(dir) {
|
||||
return path.join(__dirname, dir)
|
||||
}
|
||||
|
||||
const name = defaultSettings.title || 'vue Admin Template' // page title
|
||||
|
||||
// If your port is set to 80,
|
||||
// use administrator privileges to execute the command line.
|
||||
// For example, Mac: sudo npm run
|
||||
// You can change the port by the following methods:
|
||||
// port = 9528 npm run dev OR npm run dev --port = 9528
|
||||
const port = process.env.port || process.env.npm_config_port || 9528 // dev port
|
||||
|
||||
// All configuration item explanations can be find in https://cli.vuejs.org/config/
|
||||
module.exports = {
|
||||
/**
|
||||
* You will need to set publicPath if you plan to deploy your site under a sub path,
|
||||
* for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
|
||||
* then publicPath should be set to "/bar/".
|
||||
* In most cases please use '/' !!!
|
||||
* Detail: https://cli.vuejs.org/config/#publicpath
|
||||
*/
|
||||
publicPath: '/',
|
||||
outputDir: 'dist',
|
||||
assetsDir: 'static',
|
||||
lintOnSave: process.env.NODE_ENV === 'development',
|
||||
productionSourceMap: false,
|
||||
devServer: {
|
||||
port: port,
|
||||
open: true,
|
||||
overlay: {
|
||||
warnings: false,
|
||||
errors: true
|
||||
},
|
||||
before: require('./mock/mock-server.js')
|
||||
},
|
||||
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: name,
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve('src')
|
||||
}
|
||||
}
|
||||
},
|
||||
chainWebpack(config) {
|
||||
// it can improve the speed of the first screen, it is recommended to turn on preload
|
||||
config.plugin('preload').tap(() => [
|
||||
{
|
||||
rel: 'preload',
|
||||
// to ignore runtime.js
|
||||
// https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171
|
||||
fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
|
||||
include: 'initial'
|
||||
}
|
||||
])
|
||||
|
||||
// when there are many pages, it will cause too many meaningless requests
|
||||
config.plugins.delete('prefetch')
|
||||
|
||||
// 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()
|
||||
|
||||
config
|
||||
.when(process.env.NODE_ENV !== 'development',
|
||||
config => {
|
||||
config
|
||||
.plugin('ScriptExtHtmlWebpackPlugin')
|
||||
.after('html')
|
||||
.use('script-ext-html-webpack-plugin', [{
|
||||
// `runtime` must same as runtimeChunk name. default is `runtime`
|
||||
inline: /runtime\..*\.js$/
|
||||
}])
|
||||
.end()
|
||||
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
|
||||
},
|
||||
elementUI: {
|
||||
name: 'chunk-elementUI', // split elementUI into a single package
|
||||
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
|
||||
test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
|
||||
},
|
||||
commons: {
|
||||
name: 'chunk-commons',
|
||||
test: resolve('src/components'), // can customize your rules
|
||||
minChunks: 3, // minimum common number
|
||||
priority: 5,
|
||||
reuseExistingChunk: true
|
||||
}
|
||||
}
|
||||
})
|
||||
// https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk
|
||||
config.optimization.runtimeChunk('single')
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue