提交
This commit is contained in:
1 parent
2df2ef0808
commit
683f14828f
388 files changed
+2738488
-81306
No files matched your search
@@ -1,81 +1,118 @@
|
||||
<!-- 门户顶栏:一级横排 + 方案 A 大面板(左右分行二级/三级文字链) -->
|
||||
<!-- 门户顶栏:当前一级下的二级按宽度平铺 +「更多」+ 三级下拉(下拉 Teleport 到 body,避免被 overflow 裁切) -->
|
||||
<template>
|
||||
<div class="portal-mega-menu" ref="rootRef">
|
||||
<nav class="portal-mega-l1" aria-label="一级模块">
|
||||
<div class="portal-flat-nav" ref="rootRef">
|
||||
<!-- 离屏测量:用于按宽度计算可见项,不参与布局 -->
|
||||
<div ref="measureRef" class="portal-flat-nav__measure" aria-hidden="true">
|
||||
<button
|
||||
v-for="item in moduleList"
|
||||
:key="item.path"
|
||||
v-for="item in l2Items"
|
||||
:key="`m-${item.path || item.meta?.title}`"
|
||||
type="button"
|
||||
class="portal-mega-l1__item"
|
||||
:class="{
|
||||
'is-selected': isL1Selected(item)
|
||||
}"
|
||||
@click="toggleModule(item)"
|
||||
class="portal-flat-nav__item portal-flat-nav__measure-item"
|
||||
>
|
||||
<span>{{ item.formattedTitle }}</span>
|
||||
<span class="portal-mega-l1__caret">{{ openModulePath === item.path ? '▴' : '▾' }}</span>
|
||||
<span>{{ formatMenuTitle(item.meta.title) }}</span>
|
||||
<span v-if="l2HasExpandableChildren(item)" class="portal-flat-nav__caret">▾</span>
|
||||
</button>
|
||||
<button type="button" class="portal-flat-nav__item portal-flat-nav__measure-more">
|
||||
<span>更多</span>
|
||||
<span class="portal-flat-nav__caret">▾</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav v-if="l2Items.length" class="portal-flat-nav__bar" aria-label="二级菜单">
|
||||
<div
|
||||
v-for="item in visibleItems"
|
||||
:key="item.path || item.meta?.title"
|
||||
class="portal-flat-nav__item-wrap"
|
||||
>
|
||||
<button
|
||||
:ref="(el) => setL2TriggerRef(itemKey(item), el)"
|
||||
type="button"
|
||||
class="portal-flat-nav__item"
|
||||
:class="{
|
||||
'is-active': isL2Active(item),
|
||||
'is-open': openL2Key === itemKey(item)
|
||||
}"
|
||||
@click="handleL2Click(item)"
|
||||
>
|
||||
<span>{{ formatMenuTitle(item.meta.title) }}</span>
|
||||
<span v-if="l2HasExpandableChildren(item)" class="portal-flat-nav__caret">
|
||||
{{ openL2Key === itemKey(item) ? '▴' : '▾' }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="overflowItems.length" class="portal-flat-nav__item-wrap">
|
||||
<button
|
||||
ref="moreTriggerRef"
|
||||
type="button"
|
||||
class="portal-flat-nav__item"
|
||||
:class="{
|
||||
'is-active': overflowHasActive,
|
||||
'is-open': moreOpen
|
||||
}"
|
||||
@click="toggleMore"
|
||||
>
|
||||
<span>更多</span>
|
||||
<span class="portal-flat-nav__caret">{{ moreOpen ? '▴' : '▾' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
<p v-else class="portal-flat-nav__empty">暂无菜单</p>
|
||||
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="openModule"
|
||||
class="portal-mega-overlay"
|
||||
@click="closePanel"
|
||||
/>
|
||||
<div
|
||||
v-if="openModule"
|
||||
class="portal-mega-panel"
|
||||
role="dialog"
|
||||
aria-label="子菜单"
|
||||
:style="panelStyle"
|
||||
v-if="openL2Item"
|
||||
ref="l2FlyoutRef"
|
||||
class="portal-flat-nav-flyout"
|
||||
role="menu"
|
||||
:style="l2FlyoutStyle"
|
||||
>
|
||||
<div class="portal-mega-panel__body">
|
||||
<div class="portal-mega-split" :class="{ 'is-single': isSingleColumn }">
|
||||
<div class="portal-mega-pane">
|
||||
<div
|
||||
v-for="row in columns.left"
|
||||
:key="row.l2.path || row.l2.meta?.title"
|
||||
class="portal-mega-row"
|
||||
>
|
||||
<div class="portal-mega-row__l2">{{ formatMenuTitle(row.l2.meta.title) }}</div>
|
||||
<div class="portal-mega-row__l3">
|
||||
<button
|
||||
v-for="link in row.l3Links"
|
||||
:key="link.path || link.meta?.title"
|
||||
type="button"
|
||||
class="portal-mega-link"
|
||||
:class="{ 'is-active': isLinkActive(link) }"
|
||||
@click="handleLinkClick(link)"
|
||||
>
|
||||
{{ formatMenuTitle(link.meta.title) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="!columns.left.length" class="portal-mega-empty">暂无菜单</p>
|
||||
</div>
|
||||
<div v-if="!isSingleColumn" class="portal-mega-pane portal-mega-pane--right">
|
||||
<div
|
||||
v-for="row in columns.right"
|
||||
:key="row.l2.path || row.l2.meta?.title"
|
||||
class="portal-mega-row"
|
||||
>
|
||||
<div class="portal-mega-row__l2">{{ formatMenuTitle(row.l2.meta.title) }}</div>
|
||||
<div class="portal-mega-row__l3">
|
||||
<button
|
||||
v-for="link in row.l3Links"
|
||||
:key="link.path || link.meta?.title"
|
||||
type="button"
|
||||
class="portal-mega-link"
|
||||
:class="{ 'is-active': isLinkActive(link) }"
|
||||
@click="handleLinkClick(link)"
|
||||
>
|
||||
{{ formatMenuTitle(link.meta.title) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
v-for="link in resolveMegaPanelL3Links(openL2Item)"
|
||||
:key="link.path || link.meta?.title"
|
||||
type="button"
|
||||
class="portal-flat-nav-flyout__opt"
|
||||
:class="{ 'is-active': isLinkActive(link) }"
|
||||
@click.stop="handleLinkClick(link)"
|
||||
>
|
||||
{{ formatMenuTitle(link.meta.title) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="moreOpen"
|
||||
ref="moreFlyoutRef"
|
||||
class="portal-flat-nav-flyout portal-flat-nav-flyout--more"
|
||||
role="menu"
|
||||
:style="moreFlyoutStyle"
|
||||
>
|
||||
<div
|
||||
v-for="item in overflowItems"
|
||||
:key="item.path || item.meta?.title"
|
||||
class="portal-flat-nav-flyout__block"
|
||||
>
|
||||
<template v-if="l2HasExpandableChildren(item)">
|
||||
<div class="portal-flat-nav-flyout__group">{{ formatMenuTitle(item.meta.title) }}</div>
|
||||
<button
|
||||
v-for="link in resolveMegaPanelL3Links(item)"
|
||||
:key="link.path || link.meta?.title"
|
||||
type="button"
|
||||
class="portal-flat-nav-flyout__opt portal-flat-nav-flyout__opt--sub"
|
||||
:class="{ 'is-active': isLinkActive(link) }"
|
||||
@click.stop="handleLinkClick(link)"
|
||||
>
|
||||
{{ formatMenuTitle(link.meta.title) }}
|
||||
</button>
|
||||
</template>
|
||||
<button
|
||||
v-else
|
||||
type="button"
|
||||
class="portal-flat-nav-flyout__opt"
|
||||
:class="{ 'is-active': isL2Active(item) }"
|
||||
@click.stop="handleLeafL2Click(item)"
|
||||
>
|
||||
{{ formatMenuTitle(item.meta.title) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
@@ -83,145 +120,255 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import {
|
||||
computed,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
watch,
|
||||
type ComponentPublicInstance
|
||||
} from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { onClickOutside } from '@vueuse/core'
|
||||
import { onClickOutside, useResizeObserver } from '@vueuse/core'
|
||||
import { formatMenuTitle } from '@/utils/router'
|
||||
import { handleMenuJump } from '@/utils/navigation'
|
||||
import { useMenuStore } from '@/store/modules/menu'
|
||||
import {
|
||||
buildMegaPanelColumns,
|
||||
calcPortalFlatNavVisibleCount,
|
||||
filterPortalTopModules,
|
||||
isMegaPanelLinkActive,
|
||||
isPortalModuleActive,
|
||||
type PortalMegaPanelColumns
|
||||
isPortalL2Active,
|
||||
l2HasExpandableChildren,
|
||||
listPortalFlatNavL2Items,
|
||||
PORTAL_DEFAULT_MODULE_PATH,
|
||||
resolveActivePortalModule,
|
||||
resolveMegaPanelL3Links
|
||||
} from '@/utils/router/portal-menu'
|
||||
import type { AppRouteRecord } from '@/types/router'
|
||||
import type { CSSProperties } from 'vue'
|
||||
|
||||
defineOptions({ name: 'ArtPortalMegaMenu' })
|
||||
|
||||
interface PortalModuleItem extends AppRouteRecord {
|
||||
isRouteActive: boolean
|
||||
formattedTitle: string
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const menuStore = useMenuStore()
|
||||
const { menuList } = storeToRefs(menuStore)
|
||||
|
||||
const rootRef = ref<HTMLElement | null>(null)
|
||||
const openModulePath = ref<string | null>(null)
|
||||
const panelStyle = ref<CSSProperties>({})
|
||||
const measureRef = ref<HTMLElement | null>(null)
|
||||
const moreTriggerRef = ref<HTMLElement | null>(null)
|
||||
const l2FlyoutRef = ref<HTMLElement | null>(null)
|
||||
const moreFlyoutRef = ref<HTMLElement | null>(null)
|
||||
const l2TriggerRefs = ref<Record<string, HTMLElement | null>>({})
|
||||
|
||||
const openL2Key = ref<string | null>(null)
|
||||
const moreOpen = ref(false)
|
||||
const visibleCount = ref(0)
|
||||
const l2FlyoutStyle = ref<CSSProperties>({})
|
||||
const moreFlyoutStyle = ref<CSSProperties>({})
|
||||
|
||||
const currentRoutePath = computed(() => String(route.meta.activePath || route.path))
|
||||
|
||||
const moduleList = computed<PortalModuleItem[]>(() => {
|
||||
return filterPortalTopModules(menuList.value).map((item) => ({
|
||||
...item,
|
||||
isRouteActive: isPortalModuleActive(item, currentRoutePath.value),
|
||||
formattedTitle: formatMenuTitle(item.meta.title)
|
||||
}))
|
||||
const activeModule = computed(() => {
|
||||
const matched = resolveActivePortalModule(menuList.value, currentRoutePath.value)
|
||||
if (matched) {
|
||||
return matched
|
||||
}
|
||||
const tops = filterPortalTopModules(menuList.value)
|
||||
return tops.find((item) => item.path === PORTAL_DEFAULT_MODULE_PATH) || tops[0]
|
||||
})
|
||||
|
||||
const openModule = computed(() => {
|
||||
if (!openModulePath.value) return undefined
|
||||
return moduleList.value.find((item) => item.path === openModulePath.value)
|
||||
})
|
||||
const l2Items = computed(() => listPortalFlatNavL2Items(activeModule.value))
|
||||
|
||||
const columns = computed<PortalMegaPanelColumns>(() =>
|
||||
buildMegaPanelColumns(openModule.value)
|
||||
const visibleItems = computed(() => l2Items.value.slice(0, visibleCount.value))
|
||||
|
||||
const overflowItems = computed(() => l2Items.value.slice(visibleCount.value))
|
||||
|
||||
const overflowHasActive = computed(() =>
|
||||
overflowItems.value.some((item) => isPortalL2Active(item, currentRoutePath.value))
|
||||
)
|
||||
|
||||
const isSingleColumn = computed(() => columns.value.right.length === 0)
|
||||
const openL2Item = computed(() => {
|
||||
if (!openL2Key.value) return undefined
|
||||
return visibleItems.value.find((item) => itemKey(item) === openL2Key.value)
|
||||
})
|
||||
|
||||
/** 与顶栏中间槽上限一致;一级较少时保证两栏二级可读 */
|
||||
const PANEL_MIN_WIDTH = 720
|
||||
const PANEL_EDGE_PAD = 16
|
||||
const itemKey = (item: AppRouteRecord): string => String(item.path || item.meta?.title || '')
|
||||
|
||||
const syncPanelPosition = (): void => {
|
||||
const el = rootRef.value
|
||||
if (!el) return
|
||||
const rect = el.getBoundingClientRect()
|
||||
const viewportW = window.innerWidth
|
||||
const minWidth = Math.min(PANEL_MIN_WIDTH, Math.max(0, viewportW - PANEL_EDGE_PAD * 2))
|
||||
const width = Math.max(rect.width, minWidth)
|
||||
const centerX = rect.left + rect.width / 2
|
||||
let left = centerX - width / 2
|
||||
left = Math.max(PANEL_EDGE_PAD, Math.min(left, viewportW - width - PANEL_EDGE_PAD))
|
||||
panelStyle.value = {
|
||||
top: `${rect.bottom}px`,
|
||||
left: `${left}px`,
|
||||
width: `${width}px`
|
||||
const setL2TriggerRef = (key: string, el: Element | ComponentPublicInstance | null): void => {
|
||||
if (!el) {
|
||||
delete l2TriggerRefs.value[key]
|
||||
return
|
||||
}
|
||||
l2TriggerRefs.value[key] = el as HTMLElement
|
||||
}
|
||||
|
||||
const closePanel = (): void => {
|
||||
openModulePath.value = null
|
||||
}
|
||||
|
||||
const toggleModule = async (item: PortalModuleItem): Promise<void> => {
|
||||
const path = String(item.path || '')
|
||||
if (!path) return
|
||||
openModulePath.value = openModulePath.value === path ? null : path
|
||||
if (openModulePath.value) {
|
||||
await nextTick()
|
||||
syncPanelPosition()
|
||||
}
|
||||
}
|
||||
|
||||
/** 顶栏同时只高亮一个一级:面板展开时跟展开项,否则跟当前路由 */
|
||||
const isL1Selected = (item: PortalModuleItem): boolean => {
|
||||
if (openModulePath.value) {
|
||||
return openModulePath.value === item.path
|
||||
}
|
||||
return item.isRouteActive
|
||||
const isL2Active = (item: AppRouteRecord): boolean => {
|
||||
return isPortalL2Active(item, currentRoutePath.value)
|
||||
}
|
||||
|
||||
const isLinkActive = (link: AppRouteRecord): boolean => {
|
||||
return isMegaPanelLinkActive(link, currentRoutePath.value)
|
||||
}
|
||||
|
||||
const closePopovers = (): void => {
|
||||
openL2Key.value = null
|
||||
moreOpen.value = false
|
||||
}
|
||||
|
||||
const clampFlyoutLeft = (left: number, width: number): number => {
|
||||
const pad = 8
|
||||
const maxLeft = Math.max(pad, window.innerWidth - width - pad)
|
||||
return Math.min(Math.max(left, pad), maxLeft)
|
||||
}
|
||||
|
||||
const syncL2FlyoutPosition = async (): Promise<void> => {
|
||||
const key = openL2Key.value
|
||||
if (!key) return
|
||||
await nextTick()
|
||||
const trigger = l2TriggerRefs.value[key]
|
||||
const flyout = l2FlyoutRef.value
|
||||
if (!trigger || !flyout) return
|
||||
|
||||
const rect = trigger.getBoundingClientRect()
|
||||
const flyoutWidth = flyout.offsetWidth || 160
|
||||
const left = clampFlyoutLeft(rect.left + rect.width / 2 - flyoutWidth / 2, flyoutWidth)
|
||||
l2FlyoutStyle.value = {
|
||||
top: `${rect.bottom - 2}px`,
|
||||
left: `${left}px`
|
||||
}
|
||||
}
|
||||
|
||||
const syncMoreFlyoutPosition = async (): Promise<void> => {
|
||||
if (!moreOpen.value) return
|
||||
await nextTick()
|
||||
const trigger = moreTriggerRef.value
|
||||
const flyout = moreFlyoutRef.value
|
||||
if (!trigger || !flyout) return
|
||||
|
||||
const rect = trigger.getBoundingClientRect()
|
||||
const flyoutWidth = flyout.offsetWidth || 180
|
||||
const left = clampFlyoutLeft(rect.right - flyoutWidth, flyoutWidth)
|
||||
moreFlyoutStyle.value = {
|
||||
top: `${rect.bottom - 2}px`,
|
||||
left: `${left}px`
|
||||
}
|
||||
}
|
||||
|
||||
const toggleMore = async (): Promise<void> => {
|
||||
moreOpen.value = !moreOpen.value
|
||||
openL2Key.value = null
|
||||
if (moreOpen.value) {
|
||||
await syncMoreFlyoutPosition()
|
||||
}
|
||||
}
|
||||
|
||||
const recomputeVisibleCount = (): void => {
|
||||
const root = rootRef.value
|
||||
const measure = measureRef.value
|
||||
const items = l2Items.value
|
||||
if (!root || !measure || !items.length) {
|
||||
visibleCount.value = 0
|
||||
return
|
||||
}
|
||||
|
||||
const availableWidth = root.clientWidth
|
||||
const itemEls = measure.querySelectorAll<HTMLElement>('.portal-flat-nav__measure-item')
|
||||
const moreEl = measure.querySelector<HTMLElement>('.portal-flat-nav__measure-more')
|
||||
if (!itemEls.length || !moreEl) {
|
||||
visibleCount.value = items.length
|
||||
return
|
||||
}
|
||||
|
||||
const itemWidths = Array.from(itemEls).map((el) => el.getBoundingClientRect().width)
|
||||
const moreWidth = moreEl.getBoundingClientRect().width
|
||||
visibleCount.value = calcPortalFlatNavVisibleCount(itemWidths, moreWidth, availableWidth)
|
||||
}
|
||||
|
||||
/** 有三级:只展开下拉;无三级:直接跳转 */
|
||||
const handleL2Click = async (item: AppRouteRecord): Promise<void> => {
|
||||
if (l2HasExpandableChildren(item)) {
|
||||
const key = itemKey(item)
|
||||
openL2Key.value = openL2Key.value === key ? null : key
|
||||
moreOpen.value = false
|
||||
if (openL2Key.value) {
|
||||
await syncL2FlyoutPosition()
|
||||
}
|
||||
return
|
||||
}
|
||||
closePopovers()
|
||||
handleMenuJump(item)
|
||||
}
|
||||
|
||||
const handleLeafL2Click = (item: AppRouteRecord): void => {
|
||||
closePopovers()
|
||||
handleMenuJump(item)
|
||||
}
|
||||
|
||||
const handleLinkClick = (link: AppRouteRecord): void => {
|
||||
closePanel()
|
||||
closePopovers()
|
||||
handleMenuJump(link)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.fullPath,
|
||||
() => {
|
||||
closePanel()
|
||||
closePopovers()
|
||||
}
|
||||
)
|
||||
|
||||
watch(openModulePath, async (path) => {
|
||||
if (!path) return
|
||||
await nextTick()
|
||||
syncPanelPosition()
|
||||
watch(
|
||||
l2Items,
|
||||
async () => {
|
||||
closePopovers()
|
||||
await nextTick()
|
||||
recomputeVisibleCount()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
useResizeObserver(rootRef, () => {
|
||||
recomputeVisibleCount()
|
||||
if (openL2Key.value) {
|
||||
void syncL2FlyoutPosition()
|
||||
}
|
||||
if (moreOpen.value) {
|
||||
void syncMoreFlyoutPosition()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', syncPanelPosition)
|
||||
window.addEventListener('scroll', syncPanelPosition, true)
|
||||
const syncOpenFlyoutPositions = (): void => {
|
||||
if (openL2Key.value) void syncL2FlyoutPosition()
|
||||
if (moreOpen.value) void syncMoreFlyoutPosition()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
recomputeVisibleCount()
|
||||
window.addEventListener('resize', syncOpenFlyoutPositions)
|
||||
window.addEventListener('scroll', syncOpenFlyoutPositions, true)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', syncPanelPosition)
|
||||
window.removeEventListener('scroll', syncPanelPosition, true)
|
||||
window.removeEventListener('resize', syncOpenFlyoutPositions)
|
||||
window.removeEventListener('scroll', syncOpenFlyoutPositions, true)
|
||||
})
|
||||
|
||||
onClickOutside(rootRef, (event) => {
|
||||
const target = event.target as HTMLElement | null
|
||||
if (target?.closest?.('.portal-mega-panel')) {
|
||||
return
|
||||
onClickOutside(
|
||||
rootRef,
|
||||
() => {
|
||||
closePopovers()
|
||||
},
|
||||
{
|
||||
ignore: [l2FlyoutRef, moreFlyoutRef]
|
||||
}
|
||||
closePanel()
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.portal-mega-menu {
|
||||
.portal-flat-nav {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
@@ -230,181 +377,147 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.portal-mega-l1 {
|
||||
.portal-flat-nav__measure {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
align-items: stretch;
|
||||
height: 56px;
|
||||
pointer-events: none;
|
||||
visibility: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.portal-flat-nav__bar {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 2px;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
max-height: 100%;
|
||||
/* 仅允许横向溢出;若只写 overflow-x:auto,overflow-y 会被算成 auto,易出现竖向滚动条 */
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: thin;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.portal-mega-l1__item {
|
||||
.portal-flat-nav__item-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: stretch;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.portal-flat-nav__item {
|
||||
box-sizing: border-box;
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
max-height: 56px;
|
||||
padding: 0 14px;
|
||||
padding: 0 12px;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
color: #fff;
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-bottom: 4px solid transparent;
|
||||
border-bottom: 2px solid transparent;
|
||||
transition:
|
||||
background-color 0.15s ease,
|
||||
color 0.15s ease,
|
||||
border-color 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
&.is-active,
|
||||
&.is-open {
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.14);
|
||||
background: rgba(255, 255, 255, 0.16);
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.portal-mega-l1__caret {
|
||||
.portal-flat-nav__caret {
|
||||
font-size: 10px;
|
||||
opacity: 0.75;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.portal-flat-nav__empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.portal-mega-overlay {
|
||||
.portal-flat-nav-flyout {
|
||||
position: fixed;
|
||||
inset: 56px 0 0 0;
|
||||
z-index: 190;
|
||||
background: rgba(15, 23, 42, 0.25);
|
||||
}
|
||||
|
||||
.portal-mega-panel {
|
||||
position: fixed;
|
||||
z-index: 200;
|
||||
z-index: 3200;
|
||||
box-sizing: border-box;
|
||||
max-height: min(720px, calc(100vh - 72px));
|
||||
min-width: 160px;
|
||||
max-height: min(360px, calc(100vh - 72px));
|
||||
padding: 6px;
|
||||
overflow: auto;
|
||||
background: #fff;
|
||||
border: 1px solid var(--mint-border, #e2e8f0);
|
||||
border-top: none;
|
||||
border-radius: 0 0 10px 10px;
|
||||
box-shadow: 0 14px 32px rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 28px rgba(15, 23, 42, 0.12);
|
||||
|
||||
.portal-mega-panel__body {
|
||||
padding: 12px 16px 18px;
|
||||
}
|
||||
|
||||
.portal-mega-split {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0 16px;
|
||||
align-items: stretch;
|
||||
|
||||
&.is-single {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.portal-mega-pane {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.portal-mega-pane--right {
|
||||
padding-left: 16px;
|
||||
border-left: 1px solid var(--mint-border, #e2e8f0);
|
||||
}
|
||||
|
||||
.portal-mega-row {
|
||||
display: grid;
|
||||
grid-template-columns: 110px 1fr;
|
||||
gap: 8px 14px;
|
||||
align-items: start;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.portal-mega-row__l2 {
|
||||
padding-top: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 1.35;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.portal-mega-row__l3 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
gap: 4px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.portal-mega-link {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 4px 0;
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 1.45;
|
||||
color: #0f172a;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
transition: color 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
color: var(--primary-dark, #059669);
|
||||
&--more {
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
font-weight: 600;
|
||||
color: var(--primary-dark, #059669);
|
||||
}
|
||||
}
|
||||
|
||||
.portal-mega-empty {
|
||||
margin: 12px 0;
|
||||
font-size: 13px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
@media only screen and (width <= 900px) {
|
||||
.portal-mega-split {
|
||||
grid-template-columns: 1fr;
|
||||
&__group {
|
||||
padding: 6px 10px 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.portal-mega-pane--right {
|
||||
padding-left: 0;
|
||||
border-left: none;
|
||||
}
|
||||
&__opt {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 8px 10px;
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 1.35;
|
||||
color: #1e293b;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
|
||||
.portal-mega-row {
|
||||
grid-template-columns: 96px 1fr;
|
||||
&:hover {
|
||||
color: var(--primary-dark, #059669);
|
||||
background: var(--primary-bg, #ecfdf5);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
font-weight: 600;
|
||||
color: var(--primary-dark, #059669);
|
||||
background: var(--primary-bg, #ecfdf5);
|
||||
}
|
||||
|
||||
&--sub {
|
||||
padding-left: 18px;
|
||||
color: #475569;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,18 +1,14 @@
|
||||
<!-- 门户顶栏一级模块下拉 -->
|
||||
<!-- 门户顶栏:品牌旁一级模块下拉 -->
|
||||
<template>
|
||||
<div class="portal-module-select">
|
||||
<ElDropdown
|
||||
v-if="moduleList.length"
|
||||
trigger="click"
|
||||
teleported
|
||||
popper-class="portal-module-select-popper"
|
||||
@command="handleModuleSelect"
|
||||
>
|
||||
<button type="button" class="portal-module-select-trigger">
|
||||
<ArtSvgIcon
|
||||
v-if="activeModule?.meta?.icon"
|
||||
:icon="activeModule.meta.icon"
|
||||
class="portal-module-select-trigger__icon"
|
||||
/>
|
||||
<span class="portal-module-select-trigger__label">{{ triggerLabel }}</span>
|
||||
<ElIcon class="portal-module-select-trigger__arrow"><ArrowDown /></ElIcon>
|
||||
</button>
|
||||
@@ -24,11 +20,6 @@
|
||||
:command="item.path"
|
||||
:class="{ 'is-active': item.isActive }"
|
||||
>
|
||||
<ArtSvgIcon
|
||||
v-if="item.meta.icon"
|
||||
:icon="item.meta.icon"
|
||||
class="portal-module-select-item__icon"
|
||||
/>
|
||||
<span>{{ item.formattedTitle }}</span>
|
||||
</ElDropdownItem>
|
||||
</ElDropdownMenu>
|
||||
@@ -83,7 +74,10 @@
|
||||
if (matched) {
|
||||
return matched
|
||||
}
|
||||
return moduleList.value.find((item) => item.path === PORTAL_DEFAULT_MODULE_PATH) || moduleList.value[0]
|
||||
return (
|
||||
moduleList.value.find((item) => item.path === PORTAL_DEFAULT_MODULE_PATH) ||
|
||||
moduleList.value[0]
|
||||
)
|
||||
})
|
||||
|
||||
const triggerLabel = computed(() => {
|
||||
@@ -116,49 +110,46 @@
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
max-width: 180px;
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
height: 32px;
|
||||
padding: 0 10px 0 12px;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
border: 1px solid rgba(255, 255, 255, 0.35);
|
||||
border-radius: 8px;
|
||||
|
||||
&__icon {
|
||||
flex-shrink: 0;
|
||||
font-size: 18px;
|
||||
color: var(--primary, #1ca851);
|
||||
}
|
||||
|
||||
&__label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 15px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
color: var(--mint-text, #1e293b);
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__arrow {
|
||||
flex-shrink: 0;
|
||||
font-size: 14px;
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
&:hover:not(.is-disabled) {
|
||||
background: transparent;
|
||||
background: rgba(255, 255, 255, 0.22);
|
||||
border-color: rgba(255, 255, 255, 0.55);
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
cursor: not-allowed;
|
||||
background: transparent;
|
||||
opacity: 0.72;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
/* 高于顶栏中间二级(含 Teleport 下拉),避免一级面板被挡住 */
|
||||
.portal-module-select-popper.el-popper {
|
||||
z-index: 3300 !important;
|
||||
backdrop-filter: none;
|
||||
}
|
||||
|
||||
@@ -167,17 +158,13 @@
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
font-size: 15px;
|
||||
font-size: 14px;
|
||||
|
||||
&.is-active {
|
||||
color: var(--primary, #1ca851);
|
||||
background: var(--primary-bg, #e6f8ed);
|
||||
font-weight: 600;
|
||||
color: var(--primary-dark, #059669);
|
||||
background: var(--primary-bg, #ecfdf5);
|
||||
}
|
||||
}
|
||||
|
||||
.portal-module-select-item__icon {
|
||||
flex-shrink: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -2,11 +2,11 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="showWorkTab"
|
||||
class="worktab-strip box-border flex w-full items-center px-5 mb-3 select-none max-sm:px-[15px]"
|
||||
class="worktab-strip worktab-strip--capsule box-border flex w-full items-center select-none"
|
||||
>
|
||||
<div class="worktab-scroll-wrap min-w-0 flex-1" ref="scrollRef">
|
||||
<ul
|
||||
class="float-left whitespace-nowrap !bg-transparent flex"
|
||||
class="worktab-list float-left whitespace-nowrap !bg-transparent flex"
|
||||
:class="[tabStyle === 'tab-google' ? 'pl-1' : '']"
|
||||
ref="tabsRef"
|
||||
:style="{
|
||||
@@ -15,8 +15,11 @@
|
||||
}"
|
||||
>
|
||||
<li
|
||||
class="worktab-chip inline-flex flex-cc mr-1.5 text-xs c-p group"
|
||||
:class="[item.path === activeTab ? 'is-active' : '', item.fixedTab ? 'is-fixed' : '']"
|
||||
class="worktab-chip inline-flex flex-cc text-xs c-p group"
|
||||
:class="[
|
||||
item.path === activeTab ? 'is-active' : '',
|
||||
item.fixedTab ? 'is-fixed is-home' : ''
|
||||
]"
|
||||
v-for="(item, index) in list"
|
||||
:key="item.path"
|
||||
:ref="item.path"
|
||||
@@ -24,24 +27,26 @@
|
||||
@click="clickTab(item)"
|
||||
@contextmenu.prevent="(e) => showMenu(e, item.path)"
|
||||
>
|
||||
{{ item.customTitle || formatMenuTitle(item.title) }}
|
||||
<span class="worktab-chip__label">{{
|
||||
item.customTitle || formatMenuTitle(item.title)
|
||||
}}</span>
|
||||
<span
|
||||
v-if="list.length > 1 && !item.fixedTab"
|
||||
class="worktab-close inline-flex flex-cc relative ml-0.5 p-1 rounded-full tad-200"
|
||||
class="worktab-close inline-flex flex-cc relative rounded-full tad-200"
|
||||
@click.stop="closeWorktab('current', item.path)"
|
||||
>
|
||||
<ArtSvgIcon icon="close-bold" class="text-[10px] text-inherit" />
|
||||
<ArtSvgIcon icon="close-bold" class="worktab-close__icon text-[10px]" />
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0">
|
||||
<div class="worktab-menu-wrap flex shrink-0">
|
||||
<div
|
||||
class="worktab-menu-btn flex-cc relative top-0 size-8 leading-8 text-center c-p tad-200"
|
||||
class="worktab-menu-btn flex-cc relative top-0 leading-none text-center c-p tad-200"
|
||||
@click="(e) => showMenu(e, activeTab)"
|
||||
>
|
||||
<ArtSvgIcon icon="arrow-down" class="text-2xl text-g-700" />
|
||||
<ArtSvgIcon icon="arrow-down" class="text-xl text-inherit" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -508,93 +513,167 @@
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.worktab-strip {
|
||||
/* 方案 B′:浅底胶囊居中 + 未激活白底描边(docs/portal-worktab-capsule-v2-demo.html) */
|
||||
.worktab-strip--capsule {
|
||||
position: relative;
|
||||
padding: 0 24px 4px !important;
|
||||
background: var(--mint-surface);
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
height: 48px;
|
||||
min-height: 48px;
|
||||
padding: 0 14px !important;
|
||||
margin: 0 !important;
|
||||
background: #f1f5f9;
|
||||
border-bottom: 1px solid var(--mint-border, #e2e8f0);
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.worktab-scroll-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 100%;
|
||||
overflow-x: clip;
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
.worktab-list {
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.worktab-chip {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 220px;
|
||||
padding: 10px 16px !important;
|
||||
margin-bottom: -7px;
|
||||
display: inline-flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
max-width: 160px;
|
||||
height: 32px;
|
||||
min-height: 32px;
|
||||
padding: 0 8px 0 12px !important;
|
||||
margin: 0 !important;
|
||||
overflow: hidden;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--mint-text-muted);
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
color: #475569;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
border: 1px solid #dbe3ec;
|
||||
border-radius: 8px !important;
|
||||
box-shadow: 0 1px 0 rgba(15, 23, 42, 0.02);
|
||||
transition:
|
||||
background 0.15s ease,
|
||||
color 0.15s ease,
|
||||
border-color 0.15s ease,
|
||||
box-shadow 0.15s ease;
|
||||
}
|
||||
|
||||
.worktab-chip__label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
background: var(--mint-surface);
|
||||
border: 1px solid var(--mint-border);
|
||||
border-radius: 8px 8px 0 0 !important;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.worktab-chip.is-fixed {
|
||||
padding-right: 12px !important;
|
||||
padding-left: 12px !important;
|
||||
}
|
||||
|
||||
.worktab-chip:hover {
|
||||
color: var(--mint-text);
|
||||
background: var(--mint-row-alt);
|
||||
color: var(--mint-text, #1e293b);
|
||||
background: #fff;
|
||||
border-color: #cbd5e1;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.worktab-chip.is-active {
|
||||
z-index: 2;
|
||||
color: var(--mint-primary-dark);
|
||||
border-bottom-color: var(--mint-surface);
|
||||
box-shadow: none;
|
||||
font-weight: 600;
|
||||
color: var(--mint-primary-dark, #059669);
|
||||
background: #fff;
|
||||
border-color: #6ee7b7;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(16, 185, 129, 0.12),
|
||||
0 1px 2px rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
.worktab-chip.is-active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
right: 0;
|
||||
bottom: 1px;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
background: var(--mint-primary);
|
||||
pointer-events: none;
|
||||
.worktab-chip.is-home.is-active {
|
||||
background: var(--mint-primary-bg, #ecfdf5);
|
||||
border-color: #6ee7b7;
|
||||
}
|
||||
|
||||
.worktab-close {
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: 0;
|
||||
color: #64748b;
|
||||
border-radius: 999px;
|
||||
opacity: 1;
|
||||
transition:
|
||||
background 0.15s ease,
|
||||
color 0.15s ease;
|
||||
}
|
||||
|
||||
.worktab-close__icon {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.worktab-close:hover {
|
||||
background: var(--mint-row-alt);
|
||||
color: var(--mint-text, #1e293b);
|
||||
background: #f1f5f9;
|
||||
}
|
||||
|
||||
.worktab-close:hover .worktab-close__icon {
|
||||
color: var(--mint-text, #1e293b);
|
||||
}
|
||||
|
||||
.worktab-chip.is-active .worktab-close,
|
||||
.worktab-chip.is-active .worktab-close__icon {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.worktab-menu-wrap {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.worktab-menu-btn {
|
||||
margin-left: 6px;
|
||||
color: var(--mint-text-muted);
|
||||
border: 1px solid var(--mint-border);
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
background: #fff;
|
||||
border: 1px solid #dbe3ec;
|
||||
border-radius: 8px;
|
||||
background: var(--mint-surface);
|
||||
box-shadow: 0 1px 0 rgba(15, 23, 42, 0.02);
|
||||
}
|
||||
|
||||
.worktab-menu-btn:hover {
|
||||
color: var(--mint-text);
|
||||
background: var(--mint-row-alt);
|
||||
color: var(--mint-primary-dark, #059669);
|
||||
background: var(--mint-primary-bg, #ecfdf5);
|
||||
border-color: #a7f3d0;
|
||||
}
|
||||
|
||||
@media only screen and (width <= 768px) {
|
||||
.worktab-strip {
|
||||
padding-right: 0.625rem;
|
||||
padding-left: 0.625rem;
|
||||
.worktab-strip--capsule {
|
||||
padding-right: 12px !important;
|
||||
padding-left: 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (width <= 640px) {
|
||||
.worktab-strip {
|
||||
padding-right: 0.9375rem;
|
||||
padding-left: 0.9375rem;
|
||||
.worktab-strip--capsule {
|
||||
padding-right: 10px !important;
|
||||
padding-left: 10px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div
|
||||
class="filter-date-shortcuts list-toolbar-date-shortcuts"
|
||||
:class="{ 'is-select': variant === 'select' }"
|
||||
:class="{
|
||||
'is-select': variant === 'select',
|
||||
'is-pills': variant === 'pills'
|
||||
}"
|
||||
role="group"
|
||||
:aria-label="ariaLabel"
|
||||
>
|
||||
@@ -20,6 +23,22 @@
|
||||
:value="item.type"
|
||||
/>
|
||||
</el-select>
|
||||
<div v-else-if="variant === 'pills'" class="panel-pill-tabs panel-pill-tabs--start list-toolbar-date-pills">
|
||||
<div class="panel-pill-tabs__track" role="tablist" :aria-label="ariaLabel">
|
||||
<button
|
||||
v-for="item in shortcutItems"
|
||||
:key="item.type"
|
||||
type="button"
|
||||
role="tab"
|
||||
class="panel-pill-tabs__btn"
|
||||
:class="{ 'is-active': active === item.type }"
|
||||
:aria-selected="active === item.type"
|
||||
@click="emit('change', item.type)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<el-button
|
||||
v-for="item in shortcutItems"
|
||||
@@ -42,8 +61,8 @@
|
||||
defineProps<{
|
||||
active: DateShortcutType | ''
|
||||
ariaLabel?: string
|
||||
/** buttons=五个描边按钮;select=下拉选择(列表工具栏默认) */
|
||||
variant?: 'buttons' | 'select'
|
||||
/** buttons=描边按钮;select=下拉;pills=胶囊分段 Tab(列表工具栏推荐) */
|
||||
variant?: 'buttons' | 'select' | 'pills'
|
||||
}>(),
|
||||
{
|
||||
ariaLabel: '日期快捷筛选',
|
||||
@@ -87,6 +106,15 @@
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.list-toolbar-date-shortcuts.is-pills {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding-right: 8px;
|
||||
margin-right: 4px;
|
||||
border-right: 1px solid var(--mint-border, #d9e8e2);
|
||||
}
|
||||
|
||||
.date-shortcut-select {
|
||||
width: 112px;
|
||||
}
|
||||
@@ -94,4 +122,22 @@
|
||||
.date-shortcut-select :deep(.el-select__wrapper) {
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
/* 工具栏内胶囊 Tab:紧凑尺寸,左对齐,无面板级外边距 */
|
||||
.list-toolbar-date-pills.panel-pill-tabs {
|
||||
margin: 0;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.list-toolbar-date-pills .panel-pill-tabs__track {
|
||||
gap: 2px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.list-toolbar-date-pills .panel-pill-tabs__btn {
|
||||
min-width: 0;
|
||||
height: 30px;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user