Commit 2786b410 authored by Junling Bu's avatar Junling Bu
Browse files

feat[litemall-admin]: 更新vue-element-admin到4.3.0

parent 92cfea63
{
"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;
}
......
<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
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()
})
}
......
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,
......
/* 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>
<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;
}
......
<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()
}
}
}
......
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'
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment