edit panel sms show
This commit is contained in:
@@ -5,8 +5,8 @@
|
||||
:style="`height:${layoutStore.header.height}`"
|
||||
>
|
||||
<!-- :style="`height:${layoutStore.header.height};line-height:${layoutStore.header.height}`"-->
|
||||
<div class="flex w-full min-w-0 items-center overflow-hidden">
|
||||
<div class="flex min-w-0 flex-1 items-center overflow-hidden">
|
||||
<div class="flex w-full min-w-0 items-center gap-2 overflow-hidden">
|
||||
<div class="header-menu-zone flex min-w-0 flex-1 items-center overflow-hidden">
|
||||
<img :src="getConfigApp('avatar')" style="height: 38px" class="mr-5 mt-1 shrink-0" />
|
||||
<BaseButton
|
||||
type="text"
|
||||
@@ -17,10 +17,9 @@
|
||||
/>
|
||||
<BasicMenu />
|
||||
</div>
|
||||
<!-- <BaseButton icon="vuesax-linear:menu" @click="handleCollapseSideMenu"></BaseButton>-->
|
||||
<!-- <div @click="handleCollapseSideMenu">بستن منو</div>-->
|
||||
<div class="flex shrink-0 items-center gap-3">
|
||||
<SmsCreditWidget variant="desktop" />
|
||||
<div class="header-actions flex shrink-0 items-center gap-2">
|
||||
<SmsCreditWidget variant="desktop" class="header-actions__credit min-w-0" />
|
||||
<div class="header-actions__fixed flex shrink-0 items-center gap-3">
|
||||
<ADropdown placement="bottomLeft" class="ml-5">
|
||||
<a class="ant-dropdown-link" @click.prevent>
|
||||
<!-- Hover me-->
|
||||
@@ -63,6 +62,7 @@
|
||||
v-if="layoutStore.showSettingsIcon"
|
||||
@click="handleShowSettings"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -172,7 +172,6 @@ const userStore = useUserStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const { menuTheme } = useAdminTheme()
|
||||
|
||||
|
||||
const { width } = useWindowSize()
|
||||
//my info modal
|
||||
const loading = ref<boolean>(false)
|
||||
@@ -252,6 +251,26 @@ function clearLocalStorage() {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-menu-zone {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
min-width: 0;
|
||||
flex: 1 1 0%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
flex-shrink: 0;
|
||||
background: var(--admin-bg-surface);
|
||||
padding-inline-start: 4px;
|
||||
}
|
||||
|
||||
.header-actions__credit {
|
||||
max-width: min(220px, 38vw);
|
||||
}
|
||||
|
||||
.header-avatar {
|
||||
border: 2px solid var(--admin-border-strong);
|
||||
}
|
||||
|
||||
@@ -7,58 +7,28 @@
|
||||
: `${layoutStore.layout}-menu`
|
||||
]"
|
||||
>
|
||||
<div
|
||||
ref="menuScrollRef"
|
||||
:class="[isTopMenu ? 'top-menu top-menu-scroll scroll-none min-w-0 flex-1' : '']"
|
||||
@scroll="updateScrollArrows"
|
||||
<AMenu
|
||||
:mode="layoutStore.sideMenu.mode"
|
||||
:theme="menuTheme"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
>
|
||||
<AMenu
|
||||
:mode="layoutStore.sideMenu.mode"
|
||||
:theme="menuTheme"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
:disabled-overflow="isTopMenu"
|
||||
>
|
||||
<template v-for="item in menu">
|
||||
<BasicSubMenu
|
||||
v-if="!item.meta?.hideChildren && item.children && item.children.length > 0"
|
||||
:menu-item="item"
|
||||
:key="item.name"
|
||||
:level="1"
|
||||
/>
|
||||
<BaseMenuItem v-else :item="item" :key="item.name" />
|
||||
</template>
|
||||
</AMenu>
|
||||
</div>
|
||||
|
||||
<button
|
||||
v-if="isTopMenu"
|
||||
v-show="showLeftArrow"
|
||||
type="button"
|
||||
class="top-menu-nav__btn top-menu-nav__btn--left"
|
||||
aria-label="مشاهده ادامه منو"
|
||||
@click="scrollMenu('left')"
|
||||
>
|
||||
<BaseIcon icon="vuesax-linear:arrow-left-1" size="18" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="isTopMenu"
|
||||
v-show="showRightArrow"
|
||||
type="button"
|
||||
class="top-menu-nav__btn top-menu-nav__btn--right"
|
||||
aria-label="بازگشت در منو"
|
||||
@click="scrollMenu('right')"
|
||||
>
|
||||
<BaseIcon icon="vuesax-linear:arrow-right-1" size="18" />
|
||||
</button>
|
||||
<template v-for="item in menu">
|
||||
<BasicSubMenu
|
||||
v-if="!item.meta?.hideChildren && item.children && item.children.length > 0"
|
||||
:menu-item="item"
|
||||
:key="item.name"
|
||||
:level="1"
|
||||
/>
|
||||
<BaseMenuItem v-else :item="item" :key="item.name" />
|
||||
</template>
|
||||
</AMenu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import type { ComputedRef } from 'vue'
|
||||
import { sortBy } from 'lodash'
|
||||
import { useResizeObserver } from '@vueuse/core'
|
||||
import { usePrefix } from '@core/composable/usePrefix'
|
||||
import BasicSubMenu from '@core/layouts/composnts/BasicSubMenu.vue'
|
||||
import { useLayoutStore } from '@core/stores/layout'
|
||||
@@ -67,157 +37,42 @@ import type { RouteRecordName, RouteRecordRaw } from 'vue-router'
|
||||
import { useRouterStore } from '@core/stores/router'
|
||||
import { useAdminTheme } from '@core/composable/useAdminTheme'
|
||||
|
||||
const SCROLL_STEP = 180
|
||||
const EDGE_THRESHOLD = 2
|
||||
|
||||
const { prefixCls } = usePrefix('basic-menu')
|
||||
const layoutStore = useLayoutStore()
|
||||
const currentRoute = useRoute()
|
||||
const selectedKeys = ref<(RouteRecordName | undefined)[]>([])
|
||||
const routerStore = useRouterStore()
|
||||
const { menuTheme } = useAdminTheme()
|
||||
const menuScrollRef = ref<HTMLElement | null>(null)
|
||||
const showLeftArrow = ref(false)
|
||||
const showRightArrow = ref(false)
|
||||
const rtlScrollMode = ref<'ltr' | 'negative' | 'positive'>('ltr')
|
||||
|
||||
const isTopMenu = computed(() => layoutStore.layout === 'top')
|
||||
const path: (RouteRecordName | undefined)[] = []
|
||||
|
||||
function detectRtlScrollMode(el: HTMLElement): 'ltr' | 'negative' | 'positive' {
|
||||
if (getComputedStyle(el).direction !== 'rtl') return 'ltr'
|
||||
|
||||
const max = el.scrollWidth - el.clientWidth
|
||||
if (max <= 0) return 'ltr'
|
||||
|
||||
const original = el.scrollLeft
|
||||
el.scrollLeft = 1
|
||||
const mode = el.scrollLeft === 0 ? 'positive' : 'negative'
|
||||
el.scrollLeft = original
|
||||
return mode
|
||||
}
|
||||
|
||||
function ensureRtlScrollMode(el: HTMLElement) {
|
||||
if (rtlScrollMode.value !== 'ltr') return
|
||||
rtlScrollMode.value = detectRtlScrollMode(el)
|
||||
}
|
||||
|
||||
function getNormalizedScroll(el: HTMLElement) {
|
||||
const max = Math.max(0, el.scrollWidth - el.clientWidth)
|
||||
if (max <= 1) return { max: 0, current: 0 }
|
||||
|
||||
ensureRtlScrollMode(el)
|
||||
|
||||
let current = el.scrollLeft
|
||||
if (rtlScrollMode.value === 'negative') {
|
||||
current = Math.abs(current)
|
||||
} else if (rtlScrollMode.value === 'positive') {
|
||||
current = max - current
|
||||
}
|
||||
|
||||
return {
|
||||
max,
|
||||
current: Math.max(0, Math.min(max, current))
|
||||
}
|
||||
}
|
||||
|
||||
function setNormalizedScroll(el: HTMLElement, normalized: number) {
|
||||
const max = Math.max(0, el.scrollWidth - el.clientWidth)
|
||||
const value = Math.max(0, Math.min(max, normalized))
|
||||
|
||||
if (rtlScrollMode.value === 'negative') {
|
||||
el.scrollTo({ left: -value, behavior: 'smooth' })
|
||||
return
|
||||
}
|
||||
|
||||
if (rtlScrollMode.value === 'positive') {
|
||||
el.scrollTo({ left: max - value, behavior: 'smooth' })
|
||||
return
|
||||
}
|
||||
|
||||
el.scrollTo({ left: value, behavior: 'smooth' })
|
||||
}
|
||||
|
||||
function updateScrollArrows() {
|
||||
const el = menuScrollRef.value
|
||||
if (!el || !isTopMenu.value) {
|
||||
showLeftArrow.value = false
|
||||
showRightArrow.value = false
|
||||
return
|
||||
}
|
||||
|
||||
const { max, current } = getNormalizedScroll(el)
|
||||
if (max <= 1) {
|
||||
showLeftArrow.value = false
|
||||
showRightArrow.value = false
|
||||
return
|
||||
}
|
||||
|
||||
showLeftArrow.value = current < max - EDGE_THRESHOLD
|
||||
showRightArrow.value = current > EDGE_THRESHOLD
|
||||
}
|
||||
|
||||
function scrollMenu(side: 'left' | 'right') {
|
||||
const el = menuScrollRef.value
|
||||
if (!el) return
|
||||
|
||||
const { max, current } = getNormalizedScroll(el)
|
||||
if (max <= 1) return
|
||||
|
||||
setNormalizedScroll(el, side === 'left' ? current + SCROLL_STEP : current - SCROLL_STEP)
|
||||
scheduleArrowUpdate()
|
||||
}
|
||||
|
||||
function scheduleArrowUpdate() {
|
||||
window.setTimeout(updateScrollArrows, 350)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const path: (RouteRecordName | undefined)[] = []
|
||||
|
||||
function walkInRoutes(routes: RouteRecordRaw[]) {
|
||||
for (const route of routes) {
|
||||
if (route.name == currentRoute.name) {
|
||||
path.push(route.name)
|
||||
function walkInRoutes(routes: RouteRecordRaw[]) {
|
||||
for (const route of routes) {
|
||||
if (route.name == currentRoute.name) {
|
||||
path.push(route.name)
|
||||
return true
|
||||
}
|
||||
if (route.children && route.children.length > 0) {
|
||||
path.push(route.name)
|
||||
const res = walkInRoutes(route.children)
|
||||
if (!res) {
|
||||
path.pop()
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
if (route.children && route.children.length > 0) {
|
||||
path.push(route.name)
|
||||
const res = walkInRoutes(route.children)
|
||||
if (!res) {
|
||||
path.pop()
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
walkInRoutes(routerStore.routes)
|
||||
selectedKeys.value = path
|
||||
|
||||
nextTick(updateScrollArrows)
|
||||
})
|
||||
walkInRoutes(routerStore.routes)
|
||||
selectedKeys.value = path
|
||||
|
||||
const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
|
||||
return sortBy(routerStore.routes, (item) => {
|
||||
return item.meta?.order
|
||||
}).filter((item) => !item.meta?.hideMenu)
|
||||
})
|
||||
|
||||
watch(menu, () => nextTick(updateScrollArrows), { deep: true })
|
||||
watch(isTopMenu, () => {
|
||||
rtlScrollMode.value = 'ltr'
|
||||
nextTick(updateScrollArrows)
|
||||
})
|
||||
watch(
|
||||
() => currentRoute.name,
|
||||
() => nextTick(updateScrollArrows)
|
||||
)
|
||||
|
||||
useResizeObserver(menuScrollRef, () => {
|
||||
updateScrollArrows()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@@ -231,21 +86,20 @@ useResizeObserver(menuScrollRef, () => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.top-menu-scroll .ant-menu.ant-menu-horizontal {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
width: max-content;
|
||||
.ant-menu.ant-menu-horizontal {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
border-bottom: none !important;
|
||||
padding: 0 4px !important;
|
||||
background: transparent !important;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.top-menu-scroll .ant-menu-horizontal > .ant-menu-item,
|
||||
.top-menu-scroll .ant-menu-horizontal > .ant-menu-submenu,
|
||||
.top-menu-scroll .ant-menu-horizontal > .ant-menu-submenu > .ant-menu-submenu-title {
|
||||
.ant-menu-horizontal > .ant-menu-item,
|
||||
.ant-menu-horizontal > .ant-menu-submenu,
|
||||
.ant-menu-horizontal > .ant-menu-submenu > .ant-menu-submenu-title {
|
||||
top: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
height: 36px !important;
|
||||
@@ -258,96 +112,52 @@ useResizeObserver(menuScrollRef, () => {
|
||||
transition: background-color 0.15s ease, color 0.15s ease !important;
|
||||
}
|
||||
|
||||
.top-menu-scroll .ant-menu-horizontal > .ant-menu-item::after,
|
||||
.top-menu-scroll .ant-menu-horizontal > .ant-menu-submenu::after {
|
||||
.ant-menu-horizontal > .ant-menu-item::after,
|
||||
.ant-menu-horizontal > .ant-menu-submenu::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.top-menu-scroll .ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item-selected,
|
||||
.top-menu-scroll .ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected {
|
||||
.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item-selected,
|
||||
.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected {
|
||||
background-color: var(--admin-menu-selected);
|
||||
color: var(--admin-menu-selected-text);
|
||||
}
|
||||
|
||||
.top-menu-scroll .ant-menu-horizontal.ant-menu-dark > .ant-menu-item-selected,
|
||||
.top-menu-scroll .ant-menu-horizontal.ant-menu-dark > .ant-menu-submenu-selected > .ant-menu-submenu-title {
|
||||
.ant-menu-horizontal.ant-menu-dark > .ant-menu-item-selected,
|
||||
.ant-menu-horizontal.ant-menu-dark > .ant-menu-submenu-selected > .ant-menu-submenu-title {
|
||||
background-color: var(--admin-menu-selected) !important;
|
||||
color: var(--admin-menu-selected-text) !important;
|
||||
}
|
||||
|
||||
.top-menu-scroll .ant-menu-horizontal.ant-menu-dark > .ant-menu-item-selected .ant-menu-item-icon,
|
||||
.top-menu-scroll .ant-menu-horizontal.ant-menu-dark > .ant-menu-submenu-selected .ant-menu-item-icon,
|
||||
.top-menu-scroll .ant-menu-horizontal.ant-menu-dark > .ant-menu-submenu-selected .ant-menu-submenu-arrow {
|
||||
.ant-menu-horizontal.ant-menu-dark > .ant-menu-item-selected .ant-menu-item-icon,
|
||||
.ant-menu-horizontal.ant-menu-dark > .ant-menu-submenu-selected .ant-menu-item-icon,
|
||||
.ant-menu-horizontal.ant-menu-dark > .ant-menu-submenu-selected .ant-menu-submenu-arrow {
|
||||
color: var(--admin-menu-selected-text) !important;
|
||||
}
|
||||
|
||||
.top-menu-scroll .ant-menu-horizontal.ant-menu-dark > .ant-menu-item:not(.ant-menu-item-selected):hover,
|
||||
.top-menu-scroll .ant-menu-horizontal.ant-menu-dark > .ant-menu-submenu:not(.ant-menu-submenu-selected):hover > .ant-menu-submenu-title {
|
||||
.ant-menu-horizontal.ant-menu-dark > .ant-menu-item:not(.ant-menu-item-selected):hover,
|
||||
.ant-menu-horizontal.ant-menu-dark > .ant-menu-submenu:not(.ant-menu-submenu-selected):hover > .ant-menu-submenu-title {
|
||||
background-color: var(--admin-bg-hover) !important;
|
||||
color: var(--admin-gold-light) !important;
|
||||
box-shadow: inset 0 0 0 1px var(--admin-border-strong) !important;
|
||||
}
|
||||
|
||||
.top-menu-scroll .ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item-selected::after,
|
||||
.top-menu-scroll .ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected::after {
|
||||
.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item-selected::after,
|
||||
.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.top-menu-scroll .ant-menu-horizontal .ant-menu-item-icon + .ant-menu-title-content,
|
||||
.top-menu-scroll .ant-menu-horizontal .ant-menu-item-icon + span {
|
||||
.ant-menu-horizontal .ant-menu-item-icon + .ant-menu-title-content,
|
||||
.ant-menu-horizontal .ant-menu-item-icon + span {
|
||||
margin-inline-start: 6px !important;
|
||||
margin-inline-end: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.top-menu-nav__btn {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: var(--admin-bg-elevated);
|
||||
color: var(--admin-gold-light);
|
||||
cursor: pointer;
|
||||
transform: translateY(-50%);
|
||||
box-shadow: 0 0 0 1px var(--admin-border);
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: var(--admin-bg-hover);
|
||||
}
|
||||
|
||||
&--left {
|
||||
left: 0;
|
||||
padding-right: 2px;
|
||||
background: linear-gradient(to right, var(--admin-gradient-surface) 60%, var(--admin-gradient-surface-fade));
|
||||
}
|
||||
|
||||
&--right {
|
||||
right: 0;
|
||||
padding-left: 2px;
|
||||
background: linear-gradient(to left, var(--admin-gradient-surface) 60%, var(--admin-gradient-surface-fade));
|
||||
}
|
||||
}
|
||||
|
||||
&.top-menu,
|
||||
.top-menu-scroll {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
.ant-menu-overflow-item-rest .ant-menu-submenu-title {
|
||||
height: 36px !important;
|
||||
line-height: 36px !important;
|
||||
padding: 0 10px !important;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user