新版前端UI,预发布2.6.4版本。 (#1735)
* fix: 修改 layout 中相关 emit 和 重复声明的 theme ,采用最新的vue api * fix:修改 eslintrc 的配置文件,删除大多数无用配置 * fix: 修复 layout 切换手机样式模糊层的 bug * feature: 重构 layout , 添加暗黑模式 , 删除侧边栏颜色 * fix:细节调整,activeColor摘除。 * feature:右侧滚动模式调整 * feature:无用代码剔除 * fix: 修复暗黑模式相关细节 * fix: custome config layout * feature:样式细节调整 * fix: 增加前端样式配置文件 * feature:调整基础配置,增加表格边框 * feature:buttomInfo取消底色,a标签更改为活跃色 * feature:调整阴影颜色和配置同步 * feature:版本调整2.6.3==>2.6.4 --------- Co-authored-by: bypanghu <bypanghu@163.com>
This commit is contained in:
@@ -0,0 +1,397 @@
|
||||
<!--
|
||||
@auther: bypanghu<bypanghu@163.com>
|
||||
@date: 2024/5/7
|
||||
!-->
|
||||
<template>
|
||||
<div class="gva-tabs">
|
||||
<el-tabs
|
||||
v-model="activeValue"
|
||||
:closable="!(historys.length === 1 && $route.name === defaultRouter)"
|
||||
type="card"
|
||||
class=" bg-white text-slate-700 dark:text-slate-500 dark:bg-slate-900"
|
||||
@contextmenu.prevent="openContextMenu($event)"
|
||||
@tab-click="changeTab"
|
||||
@tab-remove="removeTab"
|
||||
>
|
||||
<el-tab-pane
|
||||
v-for="item in historys"
|
||||
:key="getFmtString(item)"
|
||||
:label="item.meta.title"
|
||||
:name="getFmtString(item)"
|
||||
:tab="item"
|
||||
class="border-none "
|
||||
>
|
||||
<template #label>
|
||||
<span
|
||||
:tab="item"
|
||||
:class="activeValue === getFmtString(item) ? 'text-active' : 'text-gray-600 dark:text-slate-400 '"
|
||||
><i
|
||||
:class="activeValue === getFmtString(item) ? 'text-active' : 'text-gray-600 dark:text-slate-400'"
|
||||
/>
|
||||
{{ fmtTitle(item.meta.title,item) }}</span>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!--自定义右键菜单html代码-->
|
||||
<ul
|
||||
v-show="contextMenuVisible"
|
||||
:style="{ left: left + 'px', top: top + 'px' }"
|
||||
class="contextmenu"
|
||||
>
|
||||
<li @click="closeAll">
|
||||
关闭所有
|
||||
</li>
|
||||
<li @click="closeLeft">
|
||||
关闭左侧
|
||||
</li>
|
||||
<li @click="closeRight">
|
||||
关闭右侧
|
||||
</li>
|
||||
<li @click="closeOther">
|
||||
关闭其他
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { emitter } from '@/utils/bus.js'
|
||||
import { computed, onUnmounted, ref, watch, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useUserStore } from '@/pinia/modules/user'
|
||||
import { fmtTitle } from '@/utils/fmtRouterTitle'
|
||||
|
||||
defineOptions({
|
||||
name: 'HistoryComponent',
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const getFmtString = (item) => {
|
||||
return item.name + JSON.stringify(item.query) + JSON.stringify(item.params)
|
||||
}
|
||||
|
||||
const historys = ref([])
|
||||
const activeValue = ref('')
|
||||
const contextMenuVisible = ref(false)
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const left = ref(0)
|
||||
const top = ref(0)
|
||||
const isCollapse = ref(false)
|
||||
const isMobile = ref(false)
|
||||
const rightActive = ref('')
|
||||
const defaultRouter = computed(() => userStore.userInfo.authority.defaultRouter)
|
||||
const openContextMenu = (e) => {
|
||||
if (
|
||||
historys.value.length === 1 &&
|
||||
route.name === defaultRouter.value
|
||||
) {
|
||||
return false
|
||||
}
|
||||
let id = ''
|
||||
if (e.srcElement.nodeName === 'SPAN') {
|
||||
id = e.srcElement.offsetParent.id
|
||||
} else {
|
||||
id = e.srcElement.id
|
||||
}
|
||||
if (id) {
|
||||
contextMenuVisible.value = true
|
||||
|
||||
left.value = e.clientX
|
||||
top.value = e.clientY + 10
|
||||
rightActive.value = id.substring(4)
|
||||
}
|
||||
}
|
||||
const closeAll = () => {
|
||||
historys.value = [
|
||||
{
|
||||
name: defaultRouter.value,
|
||||
meta: {
|
||||
title: '首页',
|
||||
},
|
||||
query: {},
|
||||
params: {},
|
||||
},
|
||||
]
|
||||
router.push({ name: defaultRouter.value })
|
||||
contextMenuVisible.value = false
|
||||
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||
}
|
||||
const closeLeft = () => {
|
||||
let right
|
||||
const rightIndex = historys.value.findIndex((item) => {
|
||||
if (getFmtString(item) === rightActive.value) {
|
||||
right = item
|
||||
}
|
||||
return getFmtString(item) === rightActive.value
|
||||
})
|
||||
const activeIndex = historys.value.findIndex(
|
||||
(item) => getFmtString(item) === activeValue.value
|
||||
)
|
||||
historys.value.splice(0, rightIndex)
|
||||
if (rightIndex > activeIndex) {
|
||||
router.push(right)
|
||||
}
|
||||
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||
}
|
||||
const closeRight = () => {
|
||||
let right
|
||||
const leftIndex = historys.value.findIndex((item) => {
|
||||
if (getFmtString(item) === rightActive.value) {
|
||||
right = item
|
||||
}
|
||||
return getFmtString(item) === rightActive.value
|
||||
})
|
||||
const activeIndex = historys.value.findIndex(
|
||||
(item) => getFmtString(item) === activeValue.value
|
||||
)
|
||||
historys.value.splice(leftIndex + 1, historys.value.length)
|
||||
if (leftIndex < activeIndex) {
|
||||
router.push(right)
|
||||
}
|
||||
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||
}
|
||||
const closeOther = () => {
|
||||
let right
|
||||
historys.value = historys.value.filter((item) => {
|
||||
if (getFmtString(item) === rightActive.value) {
|
||||
right = item
|
||||
}
|
||||
return getFmtString(item) === rightActive.value
|
||||
})
|
||||
router.push(right)
|
||||
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||
}
|
||||
const isSame = (route1, route2) => {
|
||||
if (route1.name !== route2.name) {
|
||||
return false
|
||||
}
|
||||
if (Object.keys(route1.query).length !== Object.keys(route2.query).length || Object.keys(route1.params).length !== Object.keys(route2.params).length) {
|
||||
return false
|
||||
}
|
||||
for (const key in route1.query) {
|
||||
if (route1.query[key] !== route2.query[key]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for (const key in route1.params) {
|
||||
if (route1.params[key] !== route2.params[key]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
const setTab = (route) => {
|
||||
if (!historys.value.some((item) => isSame(item, route))) {
|
||||
const obj = {}
|
||||
obj.name = route.name
|
||||
obj.meta = { ...route.meta }
|
||||
delete obj.meta.matched
|
||||
obj.query = route.query
|
||||
obj.params = route.params
|
||||
historys.value.push(obj)
|
||||
}
|
||||
window.sessionStorage.setItem('activeValue', getFmtString(route))
|
||||
}
|
||||
|
||||
const historyMap = ref({})
|
||||
|
||||
const changeTab = (TabsPaneContext) => {
|
||||
const name = TabsPaneContext?.props?.name
|
||||
if (!name) return
|
||||
const tab = historyMap.value[name]
|
||||
router.push({
|
||||
name: tab.name,
|
||||
query: tab.query,
|
||||
params: tab.params,
|
||||
})
|
||||
}
|
||||
const removeTab = (tab) => {
|
||||
const index = historys.value.findIndex(
|
||||
(item) => getFmtString(item) === tab
|
||||
)
|
||||
if (getFmtString(route) === tab) {
|
||||
if (historys.value.length === 1) {
|
||||
router.push({ name: defaultRouter.value })
|
||||
} else {
|
||||
if (index < historys.value.length - 1) {
|
||||
router.push({
|
||||
name: historys.value[index + 1].name,
|
||||
query: historys.value[index + 1].query,
|
||||
params: historys.value[index + 1].params,
|
||||
})
|
||||
} else {
|
||||
router.push({
|
||||
name: historys.value[index - 1].name,
|
||||
query: historys.value[index - 1].query,
|
||||
params: historys.value[index - 1].params,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
historys.value.splice(index, 1)
|
||||
}
|
||||
|
||||
watch(() => contextMenuVisible.value, () => {
|
||||
if (contextMenuVisible.value) {
|
||||
document.body.addEventListener('click', () => {
|
||||
contextMenuVisible.value = false
|
||||
})
|
||||
} else {
|
||||
document.body.removeEventListener('click', () => {
|
||||
contextMenuVisible.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => route, (to, now) => {
|
||||
if (to.name === 'Login' || to.name === 'Reload') {
|
||||
return
|
||||
}
|
||||
historys.value = historys.value.filter((item) => !item.meta.closeTab)
|
||||
setTab(to)
|
||||
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||
activeValue.value = window.sessionStorage.getItem('activeValue')
|
||||
}, { deep: true })
|
||||
|
||||
watch(() => historys.value, () => {
|
||||
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||
historyMap.value = {}
|
||||
historys.value.forEach((item) => {
|
||||
historyMap.value[getFmtString(item)] = item
|
||||
})
|
||||
emitter.emit('setKeepAlive', historys.value)
|
||||
}, {
|
||||
deep: true
|
||||
})
|
||||
|
||||
const initPage = () => {
|
||||
// 全局监听 关闭当前页面函数
|
||||
emitter.on('closeThisPage', () => {
|
||||
removeTab(getFmtString(route))
|
||||
})
|
||||
// 全局监听 关闭所有页面函数
|
||||
emitter.on('closeAllPage', () => {
|
||||
closeAll()
|
||||
})
|
||||
emitter.on('mobile', (data) => {
|
||||
isMobile.value = data
|
||||
})
|
||||
emitter.on('collapse', (data) => {
|
||||
isCollapse.value = data
|
||||
})
|
||||
|
||||
emitter.on('setQuery', (data) => {
|
||||
const index = historys.value.findIndex(
|
||||
(item) => getFmtString(item) === activeValue.value
|
||||
)
|
||||
historys.value[index].query = data
|
||||
activeValue.value = getFmtString(historys.value[index])
|
||||
const currentUrl = window.location.href.split('?')[0]
|
||||
const currentSearchParams = new URLSearchParams(data).toString()
|
||||
window.history.replaceState({}, '', `${currentUrl}?${currentSearchParams}`)
|
||||
sessionStorage.setItem('historys', JSON.stringify(historys.value))
|
||||
})
|
||||
|
||||
emitter.on('switchTab', async(data) => {
|
||||
const index = historys.value.findIndex((item) => item.name === data.name)
|
||||
if (index < 0) {
|
||||
return
|
||||
}
|
||||
for (const key in data.query) {
|
||||
data.query[key] = String(data.query[key])
|
||||
}
|
||||
for (const key in data.params) {
|
||||
data.params[key] = String(data.params[key])
|
||||
}
|
||||
|
||||
historys.value[index].query = data.query || {}
|
||||
historys.value[index].params = data.params || {}
|
||||
await nextTick()
|
||||
router.push(historys.value[index])
|
||||
})
|
||||
const initHistorys = [
|
||||
{
|
||||
name: defaultRouter.value,
|
||||
meta: {
|
||||
title: '首页',
|
||||
},
|
||||
query: {},
|
||||
params: {},
|
||||
},
|
||||
]
|
||||
setTab(route)
|
||||
historys.value =
|
||||
JSON.parse(sessionStorage.getItem('historys')) || initHistorys
|
||||
if (!window.sessionStorage.getItem('activeValue')) {
|
||||
activeValue.value = getFmtString(route)
|
||||
} else {
|
||||
activeValue.value = window.sessionStorage.getItem('activeValue')
|
||||
}
|
||||
if (window.sessionStorage.getItem('needCloseAll') === 'true') {
|
||||
closeAll()
|
||||
window.sessionStorage.removeItem('needCloseAll')
|
||||
}
|
||||
}
|
||||
initPage()
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off('collapse')
|
||||
emitter.off('mobile')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.contextmenu {
|
||||
@apply bg-white dark:bg-slate-900 w-28 m-0 py-2.5 px-0 border border-gray-200 text-sm shadow-md rounded absolute z-50 border-solid dark:border-slate-800;
|
||||
}
|
||||
|
||||
.contextmenu li {
|
||||
@apply text-slate-700 dark:text-slate-200 text-base list-none px-4 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-600 cursor-pointer;
|
||||
}
|
||||
|
||||
$base-tag-item-height : 4rem;
|
||||
|
||||
.gva-tabs {
|
||||
::v-deep(.el-tabs--card > .el-tabs__header){
|
||||
border:none;
|
||||
}
|
||||
::v-deep(.el-tabs__nav-scroll) {
|
||||
padding: 4px 4px;
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__nav) {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__header) {
|
||||
border-bottom: 0;
|
||||
}
|
||||
::v-deep(.el-tabs__item){
|
||||
box-sizing: border-box;
|
||||
border: 1px solid var(--el-border-color-darker);
|
||||
border-radius: 2px;
|
||||
margin-right: 5px;
|
||||
margin-left: 2px;
|
||||
transition: padding 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
|
||||
height: 34px;
|
||||
&.is-active {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
::v-deep(.el-tabs__item):first-child{
|
||||
border: 1px solid var(--el-border-color-darker);
|
||||
&.is-active {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user