feat[litemall-admin]: 更新vue-element-admin到4.3.0
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "litemall-admin",
|
||||
"version": "1.0.0",
|
||||
"description": "litemall-admin basing on vue-element-admin 4.2.1",
|
||||
"description": "litemall-admin basing on vue-element-admin 4.3.0",
|
||||
"author": "linlinjava <linlinjava@163.com>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@@ -50,13 +50,14 @@
|
||||
"clipboard": "2.0.4",
|
||||
"connect": "3.6.6",
|
||||
"echarts": "4.2.1",
|
||||
"element-ui": "2.12.0",
|
||||
"element-ui": "2.13.2",
|
||||
"file-saver": "1.3.8",
|
||||
"js-cookie": "2.2.0",
|
||||
"normalize.css": "7.0.0",
|
||||
"nprogress": "0.2.0",
|
||||
"path-to-regexp": "2.4.0",
|
||||
"screenfull": "4.2.0",
|
||||
"script-loader": "0.7.2",
|
||||
"vue": "2.6.10",
|
||||
"vue-count-to": "1.0.13",
|
||||
"vue-router": "3.0.2",
|
||||
@@ -83,11 +84,10 @@
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"husky": "1.3.1",
|
||||
"lint-staged": "8.1.5",
|
||||
"node-sass": "^4.9.0",
|
||||
"sass": "^1.26.2",
|
||||
"runjs": "^4.3.2",
|
||||
"sass-loader": "^7.1.0",
|
||||
"script-ext-html-webpack-plugin": "2.1.3",
|
||||
"script-loader": "0.7.2",
|
||||
"serve-static": "^1.13.2",
|
||||
"svg-sprite-loader": "4.1.3",
|
||||
"svgo": "1.2.0",
|
||||
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/deep/ .el-badge__content.is-fixed.is-dot {
|
||||
::v-deep .el-badge__content.is-fixed.is-dot {
|
||||
right: 5px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
|
||||
<slot/>
|
||||
<slot />
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
@@ -80,7 +80,7 @@ export default {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
/deep/ {
|
||||
::v-deep {
|
||||
.el-scrollbar__bar {
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ router.beforeEach((to, from, next) => {
|
||||
store.dispatch('GetUserInfo').then(res => { // 拉取user_info
|
||||
const perms = res.data.data.perms // note: perms must be a array! such as: ['GET /aaa','POST /bbb']
|
||||
store.dispatch('GenerateRoutes', { perms }).then(() => { // 根据perms权限生成可访问的路由表
|
||||
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
|
||||
router.addRoutes(store.getters.addRoutes) // 动态添加可访问路由表
|
||||
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
|
||||
})
|
||||
}).catch((err) => {
|
||||
|
||||
@@ -24,14 +24,14 @@ import Layout from '@/views/layout/Layout'
|
||||
noCache: true if true ,the page will no be cached(default is false)
|
||||
}
|
||||
**/
|
||||
export const constantRouterMap = [
|
||||
export const constantRoutes = [
|
||||
{
|
||||
path: '/redirect',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: '/redirect/:path*',
|
||||
path: '/redirect/:path(.*)',
|
||||
component: () => import('@/views/redirect/index')
|
||||
}
|
||||
]
|
||||
@@ -71,13 +71,7 @@ export const constantRouterMap = [
|
||||
}
|
||||
]
|
||||
|
||||
export default new Router({
|
||||
// mode: 'history', // require service support
|
||||
scrollBehavior: () => ({ y: 0 }),
|
||||
routes: constantRouterMap
|
||||
})
|
||||
|
||||
export const asyncRouterMap = [
|
||||
export const asyncRoutes = [
|
||||
{
|
||||
path: '/user',
|
||||
component: Layout,
|
||||
@@ -612,3 +606,19 @@ export const asyncRouterMap = [
|
||||
|
||||
{ path: '*', redirect: '/404', hidden: true }
|
||||
]
|
||||
|
||||
const createRouter = () => new Router({
|
||||
// mode: 'history', // require service support
|
||||
scrollBehavior: () => ({ y: 0 }),
|
||||
routes: constantRoutes
|
||||
})
|
||||
|
||||
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
|
||||
|
||||
@@ -8,12 +8,9 @@ const getters = {
|
||||
token: state => state.user.token,
|
||||
avatar: state => state.user.avatar,
|
||||
name: state => state.user.name,
|
||||
introduction: state => state.user.introduction,
|
||||
status: state => state.user.status,
|
||||
roles: state => state.user.roles,
|
||||
perms: state => state.user.perms,
|
||||
setting: state => state.user.setting,
|
||||
permission_routers: state => state.permission.routers,
|
||||
addRouters: state => state.permission.addRouters
|
||||
permission_routes: state => state.permission.routes,
|
||||
addRoutes: state => state.permission.addRoutes
|
||||
}
|
||||
export default getters
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { asyncRouterMap, constantRouterMap } from '@/router'
|
||||
import { asyncRoutes, constantRoutes } from '@/router'
|
||||
|
||||
/**
|
||||
* 通过meta.perms判断是否与当前用户权限匹配
|
||||
@@ -15,16 +15,16 @@ function hasPermission(perms, route) {
|
||||
|
||||
/**
|
||||
* 递归过滤异步路由表,返回符合用户角色权限的路由表
|
||||
* @param routes asyncRouterMap
|
||||
* @param routes asyncRoutes
|
||||
* @param perms
|
||||
*/
|
||||
function filterAsyncRouter(routes, perms) {
|
||||
function filterAsyncRoutes(routes, perms) {
|
||||
const res = []
|
||||
|
||||
routes.forEach(route => {
|
||||
const tmp = { ...route }
|
||||
if (tmp.children) {
|
||||
tmp.children = filterAsyncRouter(tmp.children, perms)
|
||||
tmp.children = filterAsyncRoutes(tmp.children, perms)
|
||||
if (tmp.children && tmp.children.length > 0) {
|
||||
res.push(tmp)
|
||||
}
|
||||
@@ -40,26 +40,26 @@ function filterAsyncRouter(routes, perms) {
|
||||
|
||||
const permission = {
|
||||
state: {
|
||||
routers: constantRouterMap,
|
||||
addRouters: []
|
||||
routes: constantRoutes,
|
||||
addRoutes: []
|
||||
},
|
||||
mutations: {
|
||||
SET_ROUTERS: (state, routers) => {
|
||||
state.addRouters = routers
|
||||
state.routers = constantRouterMap.concat(routers)
|
||||
SET_ROUTES: (state, routes) => {
|
||||
state.addRoutes = routes
|
||||
state.routes = constantRoutes.concat(routes)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
GenerateRoutes({ commit }, data) {
|
||||
return new Promise(resolve => {
|
||||
const { perms } = data
|
||||
let accessedRouters
|
||||
let accessedRoutes
|
||||
if (perms.includes('*')) {
|
||||
accessedRouters = asyncRouterMap
|
||||
accessedRoutes = asyncRoutes
|
||||
} else {
|
||||
accessedRouters = filterAsyncRouter(asyncRouterMap, perms)
|
||||
accessedRoutes = filterAsyncRoutes(asyncRoutes, perms)
|
||||
}
|
||||
commit('SET_ROUTERS', accessedRouters)
|
||||
commit('SET_ROUTES', accessedRoutes)
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,38 +1,21 @@
|
||||
import { loginByUsername, logout, getUserInfo } from '@/api/login'
|
||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||
import router, { resetRouter } from '@/router'
|
||||
|
||||
const user = {
|
||||
state: {
|
||||
user: '',
|
||||
status: '',
|
||||
code: '',
|
||||
token: getToken(),
|
||||
name: '',
|
||||
avatar: '',
|
||||
introduction: '',
|
||||
roles: [],
|
||||
perms: [],
|
||||
setting: {
|
||||
articlePlatform: []
|
||||
}
|
||||
perms: []
|
||||
},
|
||||
|
||||
mutations: {
|
||||
SET_CODE: (state, code) => {
|
||||
state.code = code
|
||||
},
|
||||
SET_TOKEN: (state, token) => {
|
||||
state.token = token
|
||||
},
|
||||
SET_INTRODUCTION: (state, introduction) => {
|
||||
state.introduction = introduction
|
||||
},
|
||||
SET_SETTING: (state, setting) => {
|
||||
state.setting = setting
|
||||
},
|
||||
SET_STATUS: (state, status) => {
|
||||
state.status = status
|
||||
},
|
||||
SET_NAME: (state, name) => {
|
||||
state.name = name
|
||||
},
|
||||
@@ -78,7 +61,6 @@ const user = {
|
||||
commit('SET_ROLES', data.roles)
|
||||
commit('SET_NAME', data.name)
|
||||
commit('SET_AVATAR', data.avatar)
|
||||
commit('SET_INTRODUCTION', data.introduction)
|
||||
resolve(response)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
@@ -86,28 +68,20 @@ const user = {
|
||||
})
|
||||
},
|
||||
|
||||
// 第三方验证登录
|
||||
// LoginByThirdparty({ commit, state }, code) {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// commit('SET_CODE', code)
|
||||
// loginByThirdparty(state.status, state.email, state.code).then(response => {
|
||||
// commit('SET_TOKEN', response.data.token)
|
||||
// setToken(response.data.token)
|
||||
// resolve()
|
||||
// }).catch(error => {
|
||||
// reject(error)
|
||||
// })
|
||||
// })
|
||||
// },
|
||||
|
||||
// 登出
|
||||
LogOut({ commit, state }) {
|
||||
LogOut({ commit, state, dispatch }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout(state.token).then(() => {
|
||||
commit('SET_TOKEN', '')
|
||||
commit('SET_ROLES', [])
|
||||
commit('SET_PERMS', [])
|
||||
removeToken()
|
||||
resetRouter()
|
||||
|
||||
// reset visited views and cached views
|
||||
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
|
||||
dispatch('tagsView/delAllViews', null, { root: true })
|
||||
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
@@ -119,6 +93,7 @@ const user = {
|
||||
FedLogOut({ commit }) {
|
||||
return new Promise(resolve => {
|
||||
commit('SET_TOKEN', '')
|
||||
commit('SET_ROLES', [])
|
||||
removeToken()
|
||||
resolve()
|
||||
})
|
||||
@@ -126,19 +101,23 @@ const user = {
|
||||
|
||||
// 动态修改权限
|
||||
ChangeRoles({ commit, dispatch }, role) {
|
||||
return new Promise(resolve => {
|
||||
return new Promise(async resolve => {
|
||||
commit('SET_TOKEN', role)
|
||||
setToken(role)
|
||||
getUserInfo(role).then(response => {
|
||||
const data = response.data
|
||||
commit('SET_ROLES', data.roles)
|
||||
commit('SET_PERMS', data.perms)
|
||||
commit('SET_NAME', data.name)
|
||||
commit('SET_AVATAR', data.avatar)
|
||||
commit('SET_INTRODUCTION', data.introduction)
|
||||
dispatch('GenerateRoutes', data) // 动态修改权限后 重绘侧边菜单
|
||||
resolve()
|
||||
})
|
||||
|
||||
const { roles } = await dispatch('GetUserInfo')
|
||||
|
||||
resetRouter()
|
||||
|
||||
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
|
||||
|
||||
// dynamically add accessible routes
|
||||
router.addRoutes(accessRoutes)
|
||||
|
||||
// reset visited views and cached views
|
||||
dispatch('tagsView/delAllViews', null, { root: true })
|
||||
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
.fixed-width {
|
||||
.el-button--mini {
|
||||
padding: 7px 10px;
|
||||
width: 60px;
|
||||
min-width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* theme color */
|
||||
$--color-primary: #1890ff;
|
||||
$--color-success: #13ce66;
|
||||
$--color-warning: #FFBA00;
|
||||
$--color-warning: #ffba00;
|
||||
$--color-danger: #ff4949;
|
||||
// $--color-info: #1E1E1E;
|
||||
|
||||
@@ -17,10 +17,10 @@ $--button-font-weight: 400;
|
||||
$--border-color-light: #dfe4ed;
|
||||
$--border-color-lighter: #e6ebf5;
|
||||
|
||||
$--table-border:1px solid#dfe6ec;
|
||||
$--table-border: 1px solid #dfe6ec;
|
||||
|
||||
/* icon font path, required */
|
||||
$--font-path: '~element-ui/lib/theme-chalk/fonts';
|
||||
$--font-path: "~element-ui/lib/theme-chalk/fonts";
|
||||
|
||||
@import "~element-ui/packages/theme-chalk/src/index";
|
||||
|
||||
|
||||
@@ -57,6 +57,11 @@
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.sub-el-icon {
|
||||
margin-right: 12px;
|
||||
margin-left: -2px;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border: none;
|
||||
height: 100%;
|
||||
@@ -105,6 +110,11 @@
|
||||
.svg-icon {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.sub-el-icon {
|
||||
margin-right: 12px;
|
||||
margin-left: -2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +128,11 @@
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.sub-el-icon {
|
||||
margin-right: 12px;
|
||||
margin-left: -2px;
|
||||
}
|
||||
|
||||
.el-submenu__icon-arrow {
|
||||
display: none;
|
||||
}
|
||||
@@ -178,6 +193,10 @@
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
.sub-el-icon {
|
||||
margin-right: 12px;
|
||||
margin-left: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
.nest-menu .el-submenu>.el-submenu__title,
|
||||
|
||||
18
litemall-admin/src/vendor/Export2Excel.js
vendored
18
litemall-admin/src/vendor/Export2Excel.js
vendored
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable */
|
||||
require('script-loader!file-saver');
|
||||
import { saveAs } from 'file-saver'
|
||||
import XLSX from 'xlsx'
|
||||
|
||||
function generateArray(table) {
|
||||
@@ -145,20 +145,34 @@ export function export_table_to_excel(id) {
|
||||
}
|
||||
|
||||
export function export_json_to_excel({
|
||||
multiHeader = [],
|
||||
header,
|
||||
data,
|
||||
filename,
|
||||
merges = [],
|
||||
autoWidth = true,
|
||||
bookType= 'xlsx'
|
||||
bookType = 'xlsx'
|
||||
} = {}) {
|
||||
/* original data */
|
||||
filename = filename || 'excel-list'
|
||||
data = [...data]
|
||||
data.unshift(header);
|
||||
|
||||
for (let i = multiHeader.length - 1; i > -1; i--) {
|
||||
data.unshift(multiHeader[i])
|
||||
}
|
||||
|
||||
var ws_name = "SheetJS";
|
||||
var wb = new Workbook(),
|
||||
ws = sheet_from_array_of_arrays(data);
|
||||
|
||||
if (merges.length > 0) {
|
||||
if (!ws['!merges']) ws['!merges'] = [];
|
||||
merges.forEach(item => {
|
||||
ws['!merges'].push(XLSX.utils.decode_range(item))
|
||||
})
|
||||
}
|
||||
|
||||
if (autoWidth) {
|
||||
/*设置worksheet每列的最大宽度*/
|
||||
const colWidth = data.map(row => row.map(val => {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
<!-- 查询和其他操作 -->
|
||||
<div class="filter-container">
|
||||
<el-input v-model="listQuery.userId" clearable class="filter-item" style="width: 200px;" placeholder="请输入用户ID"/>
|
||||
<el-input v-model="listQuery.valueId" clearable class="filter-item" style="width: 200px;" placeholder="请输入商品ID"/>
|
||||
<el-input v-model="listQuery.userId" clearable class="filter-item" style="width: 200px;" placeholder="请输入用户ID" />
|
||||
<el-input v-model="listQuery.valueId" clearable class="filter-item" style="width: 200px;" placeholder="请输入商品ID" />
|
||||
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
|
||||
<el-button :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">导出</el-button>
|
||||
</div>
|
||||
@@ -12,21 +12,21 @@
|
||||
<!-- 查询结果 -->
|
||||
<el-table v-loading="listLoading" :data="list" element-loading-text="正在查询中。。。" border fit highlight-current-row>
|
||||
|
||||
<el-table-column align="center" label="用户ID" prop="userId"/>
|
||||
<el-table-column align="center" label="用户ID" prop="userId" />
|
||||
|
||||
<el-table-column align="center" label="商品ID" prop="valueId"/>
|
||||
<el-table-column align="center" label="商品ID" prop="valueId" />
|
||||
|
||||
<el-table-column align="center" label="打分" prop="star"/>
|
||||
<el-table-column align="center" label="打分" prop="star" />
|
||||
|
||||
<el-table-column align="center" label="评论内容" prop="content"/>
|
||||
<el-table-column align="center" label="评论内容" prop="content" />
|
||||
|
||||
<el-table-column align="center" label="评论图片" prop="picUrls">
|
||||
<template slot-scope="scope">
|
||||
<el-image v-for="item in scope.row.picUrls" :key="item" :src="item" :preview-src-list="scope.row.picUrls" :lazy="true" style="width: 40px; height: 40px; margin-right: 5px;"/>
|
||||
<el-image v-for="item in scope.row.picUrls" :key="item" :src="item" :preview-src-list="scope.row.picUrls" :lazy="true" style="width: 40px; height: 40px; margin-right: 5px;" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" label="时间" prop="addTime"/>
|
||||
<el-table-column align="center" label="时间" prop="addTime" />
|
||||
|
||||
<el-table-column align="center" label="操作" width="200" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
@@ -42,7 +42,7 @@
|
||||
<el-dialog :visible.sync="replyFormVisible" title="回复">
|
||||
<el-form ref="replyForm" :model="replyForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
|
||||
<el-form-item label="回复内容" prop="content">
|
||||
<el-input :autosize="{ minRows: 4, maxRows: 8}" v-model="replyForm.content" type="textarea"/>
|
||||
<el-input v-model="replyForm.content" :autosize="{ minRows: 4, maxRows: 8}" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
@@ -129,8 +129,6 @@ export default {
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
const index = this.list.indexOf(row)
|
||||
this.list.splice(index, 1)
|
||||
})
|
||||
},
|
||||
handleDownload() {
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
密码修改
|
||||
</router-link>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided>
|
||||
<span style="display:block;" @click="logout">退出</span>
|
||||
<el-dropdown-item divided @click.native="logout">
|
||||
<span style="display:block;">退出</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
@@ -17,7 +17,11 @@ export default {
|
||||
const vnodes = []
|
||||
|
||||
if (icon) {
|
||||
vnodes.push(<svg-icon icon-class={icon}/>)
|
||||
if (icon.includes('el-icon')) {
|
||||
vnodes.push(<i class={[icon, 'sub-el-icon']} />)
|
||||
} else {
|
||||
vnodes.push(<svg-icon icon-class={icon}/>)
|
||||
}
|
||||
}
|
||||
|
||||
if (title) {
|
||||
@@ -27,3 +31,11 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sub-el-icon {
|
||||
color: currentColor;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
|
||||
<template>
|
||||
<!-- eslint-disable vue/require-component-is-->
|
||||
<component v-bind="linkProps(to)">
|
||||
<slot/>
|
||||
<component :is="type" v-bind="linkProps(to)">
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isExternal } from '@/utils'
|
||||
|
||||
import { isExternal } from '@/utils/validate'
|
||||
export default {
|
||||
props: {
|
||||
to: {
|
||||
@@ -16,22 +13,28 @@ export default {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isExternalLink(routePath) {
|
||||
return isExternal(routePath)
|
||||
computed: {
|
||||
isExternal() {
|
||||
return isExternal(this.to)
|
||||
},
|
||||
linkProps(url) {
|
||||
if (this.isExternalLink(url)) {
|
||||
type() {
|
||||
if (this.isExternal) {
|
||||
return 'a'
|
||||
}
|
||||
return 'router-link'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
linkProps(to) {
|
||||
if (this.isExternal) {
|
||||
return {
|
||||
is: 'a',
|
||||
href: url,
|
||||
href: to,
|
||||
target: '_blank',
|
||||
rel: 'noopener'
|
||||
}
|
||||
}
|
||||
return {
|
||||
is: 'router-link',
|
||||
to: url
|
||||
to: to
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
text-color="#bfcbd9"
|
||||
active-text-color="#409EFF"
|
||||
>
|
||||
<sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path"/>
|
||||
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
@@ -22,7 +22,7 @@ export default {
|
||||
components: { SidebarItem },
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'permission_routers',
|
||||
'permission_routes',
|
||||
'sidebar'
|
||||
]),
|
||||
isCollapse() {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
<script>
|
||||
const tagAndTagSpacing = 4 // tagAndTagSpacing
|
||||
|
||||
export default {
|
||||
name: 'ScrollPane',
|
||||
data() {
|
||||
@@ -19,27 +18,33 @@ export default {
|
||||
return this.$refs.scrollContainer.$refs.wrap
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.scrollWrapper.addEventListener('scroll', this.emitScroll, true)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.scrollWrapper.removeEventListener('scroll', this.emitScroll)
|
||||
},
|
||||
methods: {
|
||||
handleScroll(e) {
|
||||
const eventDelta = e.wheelDelta || -e.deltaY * 40
|
||||
const $scrollWrapper = this.scrollWrapper
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
||||
},
|
||||
emitScroll() {
|
||||
this.$emit('scroll')
|
||||
},
|
||||
moveToTarget(currentTag) {
|
||||
const $container = this.$refs.scrollContainer.$el
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $scrollWrapper = this.scrollWrapper
|
||||
const tagList = this.$parent.$refs.tag
|
||||
|
||||
let firstTag = null
|
||||
let lastTag = null
|
||||
|
||||
// find first tag and last tag
|
||||
if (tagList.length > 0) {
|
||||
firstTag = tagList[0]
|
||||
lastTag = tagList[tagList.length - 1]
|
||||
}
|
||||
|
||||
if (firstTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = 0
|
||||
} else if (lastTag === currentTag) {
|
||||
@@ -49,13 +54,10 @@ export default {
|
||||
const currentIndex = tagList.findIndex(item => item === currentTag)
|
||||
const prevTag = tagList[currentIndex - 1]
|
||||
const nextTag = tagList[currentIndex + 1]
|
||||
|
||||
// the tag's offsetLeft after of nextTag
|
||||
const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
|
||||
|
||||
// the tag's offsetLeft before of prevTag
|
||||
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing
|
||||
|
||||
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
|
||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
||||
@@ -73,7 +75,7 @@ export default {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
/deep/ {
|
||||
::v-deep {
|
||||
.el-scrollbar__bar {
|
||||
bottom: 0px;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div id="tags-view-container" class="tags-view-container">
|
||||
<scroll-pane ref="scrollPane" class="tags-view-wrapper">
|
||||
<scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
|
||||
<router-link
|
||||
v-for="tag in visitedViews"
|
||||
ref="tag"
|
||||
@@ -17,10 +17,10 @@
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
|
||||
<li @click="refreshSelectedTag(selectedTag)">刷新</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭</li>
|
||||
<li @click="closeOthersTags">关闭其他</li>
|
||||
<li @click="closeAllTags(selectedTag)">关闭所有</li>
|
||||
<li @click="refreshSelectedTag(selectedTag)">Refresh</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">Close</li>
|
||||
<li @click="closeOthersTags">Close Others</li>
|
||||
<li @click="closeAllTags(selectedTag)">Close All</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
@@ -28,7 +28,6 @@
|
||||
<script>
|
||||
import ScrollPane from './ScrollPane'
|
||||
import path from 'path'
|
||||
|
||||
export default {
|
||||
components: { ScrollPane },
|
||||
data() {
|
||||
@@ -45,7 +44,7 @@ export default {
|
||||
return this.$store.state.tagsView.visitedViews
|
||||
},
|
||||
routes() {
|
||||
return this.$store.state.permission.routers
|
||||
return this.$store.state.permission.routes
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -176,19 +175,20 @@ export default {
|
||||
const offsetWidth = this.$el.offsetWidth // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||
const left = e.clientX - offsetLeft + 15 // 15: margin right
|
||||
|
||||
if (left > maxLeft) {
|
||||
this.left = maxLeft
|
||||
} else {
|
||||
this.left = left
|
||||
}
|
||||
|
||||
this.top = e.clientY
|
||||
this.visible = true
|
||||
this.selectedTag = tag
|
||||
},
|
||||
closeMenu() {
|
||||
this.visible = false
|
||||
},
|
||||
handleScroll() {
|
||||
this.closeMenu()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export { default as AppMain } from './AppMain'
|
||||
export { default as Navbar } from './Navbar'
|
||||
export { default as Sidebar } from './Sidebar/index.vue'
|
||||
export { default as TagsView } from './TagsView'
|
||||
export { default as AppMain } from './AppMain'
|
||||
export { default as TagsView } from './TagsView/index.vue'
|
||||
|
||||
@@ -29,7 +29,12 @@ module.exports = {
|
||||
lintOnSave: process.env.NODE_ENV === 'development',
|
||||
productionSourceMap: false,
|
||||
devServer: {
|
||||
port: port
|
||||
port: port,
|
||||
open: true,
|
||||
overlay: {
|
||||
warnings: false,
|
||||
errors: true
|
||||
}
|
||||
},
|
||||
configureWebpack: {
|
||||
// provide the app's title in webpack's name field, so that
|
||||
@@ -42,8 +47,11 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
chainWebpack(config) {
|
||||
config.plugins.delete('preload') // TODO: need test
|
||||
config.plugins.delete('prefetch') // TODO: need test
|
||||
// it can improve the speed of the first screen, it is recommended to turn on preload
|
||||
// config.plugins.delete('preload')
|
||||
|
||||
// when there are many pages, it will cause too many meaningless requests
|
||||
config.plugins.delete('prefetch') //
|
||||
|
||||
// set svg-sprite-loader
|
||||
config.module
|
||||
@@ -73,12 +81,6 @@ module.exports = {
|
||||
})
|
||||
.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 => {
|
||||
|
||||
Reference in New Issue
Block a user