1404/11/04- first

This commit is contained in:
2026-01-24 17:45:54 +03:30
commit edf1824229
554 changed files with 59581 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
<template>
<ADrawer
:class="prefixCls"
v-model:visible="settingsStore.showSettingsDrawer"
title="تنظیمات"
:placement="settingsStore.settingsPlacementDrawer"
>
<template #closeIcon>
<BaseIcon icon="vuesax-linear:add" size="24" class="rotate-45" />
</template>
<div class="flex flex-col gap-4">
<div class="flex justify-between">
<span class="text-gray-400 text-sm">نوار کناری (Sidebar)</span>
<ASwitch :checked="true" size="small" />
</div>
<div class="flex justify-between">
<span class="text-gray-400 text-sm">تب بندی</span>
<ASwitch :checked="true" size="small" />
</div>
<div class="flex justify-between">
<span class="text-gray-400 text-sm">سر صفحه (Header)</span>
<ASwitch :checked="true" size="small" />
</div>
<div class="flex justify-between">
<span class="text-gray-400 text-sm">مسیر جاری (Breadcrumbs)</span>
<ASwitch :checked="true" size="small" />
</div>
<div class="flex justify-between">
<span class="text-gray-400 text-sm">پانویس (Footer)</span>
<ASwitch :checked="true" size="small" />
</div>
</div>
<ADivider class="!my-2" />
<div class="flex flex-col gap-3">
<span class="text-gray-400"> طرح بندی</span>
<div class="flex items-center justify-between">
<img
src="@/core/assets/img/layout/side.png"
class="layout-item"
:class="[{ active: layoutStore.layout === 'side' }]"
/>
<img
src="@/core/assets/img/layout/mix.png"
class="layout-item"
:class="[{ active: layoutStore.layout === 'mix' }]"
/>
<img
src="@/core/assets/img/layout/top.png"
class="layout-item"
:class="[{ active: layoutStore.layout === 'top' }]"
/>
</div>
</div>
</ADrawer>
</template>
<script lang="ts" setup>
import { usePrefix } from '@core/composable/usePrefix'
import { useSettingsStore } from '@core/stores/settings'
import { useLayoutStore } from '@core/stores/layout'
//const visible = ref<boolean>(false)
const { prefixCls } = usePrefix('base-settings')
const settingsStore = useSettingsStore()
const layoutStore = useLayoutStore()
</script>
<style lang="less">
@prefix:~ '@{prefixCls}-base-settings';
.@{prefix}{
.layout-item{
cursor: pointer;
border:2px solid #eee;
padding:5px;
&.active{
border-color: @primary-color;
}
}
}
</style>
+139
View File
@@ -0,0 +1,139 @@
<template>
<!-- :class="[, isMobile ? null : 'shadow']"-->
<!-- :theme="sideTheme"-->
<ALayoutSider
theme="light"
:class="[prefixCls]"
:width="layoutStore.sideMenu.width"
:collapsible="layoutStore.sideMenu.collapsible"
:collapsedWidth="layoutStore.sideMenu.collapsedWidth"
v-model:collapsed="layoutStore.sideMenu.collapsed"
:trigger="null"
>
<div
class="flex items-center logo px-4 py-2 gap-3"
:style="`height:calc(${layoutStore.header.height} - 1px );min-height:calc(${layoutStore.header.height} - 1px )`"
>
<img src="@/assets/img/saminLogo_.png" class="object-cover" style="max-height: 32px" alt="" />
<!-- <img src="@/assets/img/logo.svg" class="object-cover w-full" style="max-height: 32px" />-->
<!-- <h1 class="contents">پنل مدیریتی</h1>-->
<!-- 11111-->
<!-- <router-link to="/myDocuments/workplace">
<img src="@/assets/img/logo.png" />
<h1>{{ systemName }}</h1>
</router-link>-->
</div>
<BasicMenu class="flex-1 overflow-scroll scroll-none" />
<!-- <div class="side-version text-center flex flex-col text-xs text-slate-400 items-center">
<span>نسخه {{ version() }}</span>
<span class="flex text-center gap-1 justify-center">
<span>نسخه core</span>
<span>{{ coreVersion() }}</span>
</span>
</div>-->
<!-- <i-menu
:theme="theme"
:collapsed="collapsed"
:options="menuData"
@select="onSelect"
class="menu"
/>-->
<!-- v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
mode="inline"
theme="dark"-->
<!-- <AMenu :inline-collapsed="layoutStore.sideMenu.collapsed" mode="inline" v-if="false">
<AMenuItem key="1">
<template #icon>
<span>111</span>
&lt;!&ndash; <PieChartOutlined />&ndash;&gt;
</template>
<span>پیشخوان</span>
</AMenuItem>
<AMenuItem key="2">
<template #icon>
<span>1111</span>
<DesktopOutlined />
</template>
<span>Option 2</span>
</AMenuItem>
<AMenuItem key="3">
<template #icon>
<InboxOutlined />
</template>
<span>Option 3</span>
</AMenuItem>
<ASubMenu key="sub1">
<template #icon>
<MailOutlined />
</template>
<template #title>Navigation One</template>
<AMenuItem key="5">Option 5</AMenuItem>
<AMenuItem key="6">Option 6</AMenuItem>
<AMenuItem key="7">Option 7</AMenuItem>
<AMenuItem key="8">Option 8</AMenuItem>
</ASubMenu>
<ASubMenu key="sub2">
<template #icon>
<AppstoreOutlined />
</template>
<template #title>Navigation Two</template>
<AMenuItem key="9">Option 9</AMenuItem>
<AMenuItem key="10">Option 10</AMenuItem>
<ASubMenu key="sub3" title="Submenu">
<AMenuItem key="11">Option 11</AMenuItem>
<AMenuItem key="12">Option 12</AMenuItem>
</ASubMenu>
</ASubMenu>
</AMenu>-->
</ALayoutSider>
</template>
<script lang="ts" setup>
import { useLayoutStore } from '@core/stores/layout'
import { usePrefix } from '@core/composable/usePrefix'
import BasicMenu from '@core/layouts/composnts/BasicMenu.vue'
//import { coreVersion, version } from '../utils'
//import { isDate } from 'lodash'
const layoutStore = useLayoutStore()
const { prefixCls } = usePrefix('basic-side-menu')
</script>
<style lang="less">
@prefix:~ '@{prefixCls}-basic-side-menu';
.@{prefix}{
/* & ul{
display: none;
}*/
/*background-color: antiquewhite;*/
position: fixed;
height: 100vh;
right: 0;
top: 0;
.ant-layout-sider-children{
display: flex;
flex-direction: column;
}
.logo{
border-bottom: 1px solid #eee;
overflow: hidden;
transition: all 0.3s;
}
.side-version{
border-top: 1px solid #eee;
@apply p-2;
max-height: 50px
}
}
</style>
+247
View File
@@ -0,0 +1,247 @@
<template>
<ALayoutHeader
theme="light"
:class="[prefixCls]"
:style="`height:${layoutStore.header.height};line-height:${layoutStore.header.height}`"
>
<!-- :style="`height:${layoutStore.header.height};line-height:${layoutStore.header.height}`"-->
<div class="flex items-center">
<div class="flex-1 flex">
<img :src="getConfigApp('avatar')" style="height: 38px" class="mr-5 mt-1" />
<BaseButton
type="text"
icon="vuesax-linear:menu"
@click="handleCollapseSideMenu"
v-if="layoutStore.layout == 'side'"
/>
<!-- justify-center flex-1 -->
<!-- <div class="">-->
<BasicMenu/>
<!-- </div>-->
</div>
<!-- <BaseButton icon="vuesax-linear:menu" @click="handleCollapseSideMenu"></BaseButton>-->
<!-- <div @click="handleCollapseSideMenu">بستن منو</div>-->
<div class="flex items-center gap-3 ">
<div>
<BaseButton size="small" @click="openNewTabBalance">تراز</BaseButton>
</div>
<ADropdown placement="bottomLeft" class="ml-5">
<a class="ant-dropdown-link" @click.prevent>
<!-- Hover me-->
<div class="flex items-center gap-1 text-gray-400">
<AAvatar :src="placeholderM" />
<Icon icon="vuesax-linear:arrow-down" size="15" />
</div>
</a>
<template #overlay>
<AMenu>
<AMenuItem>
<div class="flex flex-col">
<span class="font-bold">{{ userStore.userInfo?.name }}</span>
<span>{{ userStore.userInfo?.post_user }}</span>
</div>
</AMenuItem>
<AMenuDivider />
<AMenuItem @click="myInfoVisible = true">
<div class="flex items-center gap-1">
<Icon icon="vuesax-linear:profile" size="18" />
<span>اطلاعات حساب کاربری</span>
</div>
</AMenuItem>
<AMenuItem @click="clearLocalStorage">
<div class="flex items-center gap-1">
<Icon icon="vuesax-linear:logout" size="18" />
<span>خروج</span>
</div>
</AMenuItem>
</AMenu>
</template>
</ADropdown>
<!-- todo remove -->
<div class="w-2"></div>
<BaseButton
iconSize="20"
icon="vuesax-linear:setting"
type="text"
v-if="layoutStore.showSettingsIcon"
@click="handleShowSettings"
/>
</div>
</div>
<BasicModal
v-model:visible="myInfoVisible"
width="40rem"
title="اطلاعات حساب کاربری"
@ok="ok"
:loading="loading"
@close="myInfoVisible = false"
>
<a-form :model="form" ref="formRef" autocomplete="off">
<div class="grid md:grid-cols-2 gap-4">
<a-form-item label="نام" name="fname" :rules="inputRules()">
<BasicInput v-model:value="form.fname" />
</a-form-item>
<a-form-item label="نام خانوادگی" name="lname" :rules="inputRules()">
<BasicInput v-model:value="form.lname" />
</a-form-item>
<a-form-item label="شماره موبایل" name="mobile" :rules="inputLenRules(11)">
<BasicInput v-model:value="form.mobile" type="number" />
</a-form-item>
<a-form-item label="رمز کاربری" name="password">
<BasicInput type="password" v-model:value="form.password" />
</a-form-item>
</div>
</a-form>
</BasicModal>
<!-- this is admin header-->
<!-- <div :class="['admin-header-wide', layout, pageWidth]">
<router-link
v-if="isMobile || layout === 'head'"
to="/"s
:class="['logo', isMobile ? null : 'pc', headerTheme]"
>
<img width="32" src="@/assets/img/logo.png" />
<h1 v-if="!isMobile">{{ systemName }}</h1>
</router-link>
<a-divider v-if="isMobile" type="vertical" />
<a-icon
v-if="layout !== 'head'"
class="trigger"
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
@click="toggleCollapse"
/>
<div
v-if="layout !== 'side' && !isMobile"
class="admin-header-menu"
:style="`width: ${menuWidth};`"
>
<i-menu
class="head-menu"
:theme="headerTheme"
mode="horizontal"
:options="menuData"
@select="onSelect"
/>
</div>
<div :class="['admin-header-right', headerTheme]">
<header-search class="header-item" @active="(val) => (searchActive = val)" />
<a-tooltip class="header-item" title="帮助文档" placement="bottom">
<a href="https://iczer.gitee.io/vue-antd-admin-docs/" target="_blank">
<a-icon type="question-circle-o" />
</a>
</a-tooltip>
<header-notice class="header-item" />
<header-avatar class="header-item" />
<a-dropdown class="lang header-item">
<div><a-icon type="global" /> {{ langAlias }}</div>
<a-menu @click="(val) => setLang(val.key)" :selected-keys="[lang]" slot="overlay">
<a-menu-item v-for="lang in langList" :key="lang.key">{{
lang.key.toLowerCase() + ' ' + lang.name
}}</a-menu-item>
</a-menu>
</a-dropdown>
</div>
</div>-->
</ALayoutHeader>
</template>
<script lang="ts" setup>
import {openNewTabBalance} from '@core/composable/appInfo'
import { usePrefix } from '@core/composable/usePrefix'
import { useLayoutStore } from '@core/stores/layout'
import BasicMenu from '@core/layouts/composnts/BasicMenu.vue'
import placeholderM from '@core/assets/img/avatars/placeholder-m.jpg'
import Icon from '@core/components/BaseIcon/src/Icon.vue'
import { useSettingsStore } from '@core/stores/settings'
import { useStaticApiListsStore } from '@/store/staticApisStore'
import { useUserStore } from '@/store/userStore'
import { inputRules, inputLenRules } from '@/utils/inputRules'
import type { User } from '@/model/user'
import { createUser, updateMyInformation, updateUser } from '@/api/user'
import { message } from 'ant-design-vue/es'
import { onMounted, ref } from 'vue'
import type { FormInstance } from 'ant-design-vue'
import { cloneDeep } from 'lodash'
import { useWindowSize } from '@vueuse/core'
import {getConfigApp} from "@/utils/config";
const { prefixCls } = usePrefix('basic-header')
const layoutStore = useLayoutStore()
const store = useStaticApiListsStore()
const userStore = useUserStore()
const settingsStore = useSettingsStore()
const { width } = useWindowSize()
//my info modal
const loading = ref<boolean>(false)
const myInfoVisible = ref<boolean>(false)
const formRef = ref<FormInstance>()
const form = ref<User>({
fname: '',
lname: '',
mobile: '',
post_user: '',
manager_id: '',
responsible_id: '',
section: '',
section_id: undefined,
process_id: '',
qc_id: undefined,
user_type: undefined,
permission: []
})
onMounted(() => {
form.value = cloneDeep(userStore?.userInfo) as User
})
//submit user
async function ok() {
formRef.value?.validateFields().then(async () => {
loading.value = true
try {
await updateMyInformation(form.value)
userStore.getMyInfo()
message.success('اطلاعات با موفقیت ویرایش شد')
myInfoVisible.value = false
} catch (e) {
// message.error('خطا در بارگذاری اطلاعات')
console.error(e)
} finally {
loading.value = false
}
})
}
//const temp = 12
function handleCollapseSideMenu() {
layoutStore.sideMenu.collapsed = !layoutStore.sideMenu.collapsed
}
function handleShowSettings() {
settingsStore.showSettingsDrawer = !settingsStore.showSettingsDrawer
}
function clearLocalStorage() {
localStorage.clear()
location.reload()
}
</script>
<style lang="less">
@prefix: ~'@{prefixCls}-basic-header';
.@{prefix} {
padding: unset;
//height: unset;
//@apply flex flex-col justify-center ;
//height: 64px;
//line-height: unset;
border-bottom: 2px solid #eee;
// height: @header-height;
background-color: #fff;
//@apply backdrop-blur bg-red-200;
}
</style>
+151
View File
@@ -0,0 +1,151 @@
<template>
<!-- 'admin-layout', 'beauty-scroll'-->
<ALayout
:class="[prefixCls, { 'overflow-y-scroll': !layoutStore.header.fixed }]"
class="h-screen"
>
<!-- v-model="drawerOpen"-->
<ADrawer v-if="isMobile">
<!-- aside menu
<SideMenu
:theme="theme.mode"
:menuData="menuData"
:collapsed="false"
:collapsible="false"
@menuSelect="onMenuSelect"
/>-->
</ADrawer>
<!-- v-else-if="layout === 'side' || layout === 'mix'"-->
<!-- :class="[fixedSideBar ? 'fixed-side' : '']"
:theme="theme.mode"
:menuData="sideMenuData"
:collapsed="collapsed"
:collapsible="true"-->
<BaseSideMenu v-else-if="layoutStore.layout === 'side'" />
<div
v-if="layoutStore.sideMenu.fixedSideBar && !isMobile"
:style="`width: ${layoutStore.sideMenuWidth}; min-width: ${layoutStore.sideMenuWidth};max-width: ${layoutStore.sideMenuWidth};`"
class="virtual-side-menu"
></div>
<!-- v-if="!hideSetting" v-model="showSetting"-->
<ADrawer placement="left">
<!-- <div class="setting" slot="handler">
&lt;!&ndash; <a-icon :type="showSetting ? 'close' : 'setting'" />&ndash;&gt;
</div>-->
this is setting
<!-- <setting />-->
</ADrawer>
<!-- class="admin-layout-main beauty-scroll"-->
<ALayout>
<!-- 111111 this is header-->
<BasicHeader class="hidden md:block"></BasicHeader>
<BasicMobileHeader class="md:!hidden"></BasicMobileHeader>
<!-- <admin-header
:class="[{ 'fixed-tabs': fixedTabs, 'fixed-header': fixedHeader, 'multi-page': multiPage }]"
:style="headerStyle"
:menuData="headMenuData"
:collapsed="collapsed"
@toggleCollapse="toggleCollapse"
/>-->
<!-- <ALayoutHeader
:class="[
'virtual-header',
{ 'fixed-tabs': fixedTabs, 'fixed-header': fixedHeader, 'multi-page': multiPage }
]"
v-show="fixedHeader"
></ALayoutHeader>-->
<!-- :style="`min-height: ${minHeight}px;`"-->
<ALayoutContent
class="admin-layout-content"
:class="[{ 'overflow-y-scroll': layoutStore.header.fixed }]"
>
<!-- :style="{`min-height: ${minHeight}px;`}"-->
<!-- this is content-->
<!-- <Btn1></Btn1>-->
<!-- <BaseButton />-->
<!-- <T5></T5>-->
<!-- <T6></T6>
<BaseIcon />
<BaseButton />-->
<!-- <T1></T1>
<T100 />
<T63 />-->
<!-- <T78 />-->
<!-- <BaseButton />-->
<!-- {{ temp. }}-->
<!-- {{ temp. }}-->
<!-- this is conente
<div v-for="i in 30" class="">111111111</div>-->
<!-- <div style="position: relative">
<slot></slot>
</div>-->
<RouterView v-slot="{ Component }">
<!-- mode="out-in"-->
<!-- <transition>-->
<component :is="Component" />
<!-- </transition>-->
</RouterView>
</ALayoutContent>
<!-- <LayoutFooter style="padding: 0px">
footer
&lt;!&ndash; <page-footer :link-list="footerLinks" :copyright="copyright" />&ndash;&gt;
</LayoutFooter>-->
</ALayout>
<BaseSettings />
</ALayout>
</template>
<script lang="ts" setup>
import BaseSideMenu from './BaseSideMenu.vue'
import BasicHeader from './BasicHeader.vue'
import { usePrefix } from '@core/composable/usePrefix'
import { useLayoutStore } from '@core/stores/layout'
import { isMobile } from '@core/utils/is'
import BaseSettings from '@core/layouts/BaseSettings.vue'
import BasicMobileheader from "@/components/BasicMobileHeader.vue";
const { prefixCls } = usePrefix('basic-layout')
const layoutStore = useLayoutStore()
</script>
<style lang="less">
@prefix:~'@{prefixCls}-basic-layout';
.@{prefix}{
.virtual-side-menu{
transition: all 0.2s;
}
/* .fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}*/
/*
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.25);
}
100% {
transform: scale(1);
}
}*/
}
</style>
@@ -0,0 +1,32 @@
<template>
<AMenuItem
:disabled="props.item.meta?.disabled ?? false"
:key="props.item.name"
@click="handleClickMenuItem(item)"
>
<template #icon>
<BaseIcon :size="menuIconSize" v-if="props.item.meta?.icon" :icon="props.item.meta?.icon" />
</template>
<span>{{ props.item.meta.title ?? props.item.name }}</span>
<!-- <template #title>1111</template>-->
<!-- <div class="flex"></div>-->
</AMenuItem>
</template>
<script setup lang="ts">
import { menuIconSize } from '@root/config'
import type { RouteRecordRaw } from 'vue-router'
const router = useRouter()
interface Props {
item: RouteRecordRaw
}
const props = withDefaults(defineProps<Props>(), {})
async function handleClickMenuItem(item: RouteRecordRaw) {
router.push({ name: item.name })
}
</script>
<style scoped lang="less"></style>
+292
View File
@@ -0,0 +1,292 @@
<template>
<div :class="[prefixCls, `${layoutStore.layout}-menu`]">
<!-- :selectedKeys="selectedKeys"-->
<AMenu :mode="layoutStore.sideMenu.mode" v-model:selectedKeys="selectedKeys" >
<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"
/>
<!-- <AMenuItem
v-else
:disabled="item.meta.disabled ?? false"
:key="item.name"
@click="handleClickMenuItem(item)"
>
<template #icon>
<BaseIcon :size="menuIconSize" v-if="item.meta.icon" :icon="item.meta.icon" />
</template>
<span>{{ item.meta.title ?? item.name }}</span>
&lt;!&ndash; <template #title>1111</template>&ndash;&gt;
&lt;!&ndash; <div class="flex"></div>&ndash;&gt;
</AMenuItem>-->
<BaseMenuItem v-else :item="item" :key="item.name"/>
</template>
</AMenu >
</div>
</template>
<script lang="ts" setup>
import {usePrefix} from '@core/composable/usePrefix'
//import { menuIconSize } from '@root/config'
import {computed} from 'vue'
import type {ComputedRef} from 'vue'
import {sortBy} from 'lodash'
//import { routes } from '@/core/router/routes'
import BasicSubMenu from '@core/layouts/composnts/BasicSubMenu.vue'
import {useLayoutStore} from '@core/stores/layout'
import BaseMenuItem from '@core/layouts/composnts/BaseMenuItem.vue'
import type {RouteRecordName, RouteRecordRaw} from 'vue-router'
import {useRouterStore} from '@core/stores/router'
const {prefixCls} = usePrefix('basic-menu')
const layoutStore = useLayoutStore()
const currentRoute = useRoute()
const selectedKeys = ref<(RouteRecordName | undefined)[]>([])
const routerStore = useRouterStore()
/*const selectedKeys = computed(() => {
const path: (RouteRecordName | undefined)[] = []
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
}
}
}
return false
}
walkInRoutes(routes)
return path
})*/
onMounted(() => {
//console.log('aaa')
const path: (RouteRecordName | undefined)[] = []
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
}
}
}
return false
}
walkInRoutes(routerStore.routes)
selectedKeys.value = path
// return path
})
const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
return sortBy(routerStore.routes, (item) => {
return item.meta?.order
}).filter((item) => !item.meta?.hideMenu)
})
</script>
<style lang="less">
@prefix: ~ '@{prefixCls}-basic-menu';
.@{prefix} {
&.top-menu {
@apply pr-5;
//display: inline-flex;
.ant-menu {
// @apply gap-3;
//@apply flex-inline;
//@apply grid;
}
.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item, .ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu {
padding: 0 5px;
}
/*TODO check for line bottom menu*/
.ant-menu-horizontal > .ant-menu-item::after, .ant-menu-horizontal > .ant-menu-submenu::after {
right: 0 !important;
left: 0 !important;
}
.ant-menu-horizontal > .ant-menu-item-selected::after, .ant-menu-horizontal > .ant-menu-submenu-selected::after {
position: absolute;
bottom: 0;
border-bottom: 2px solid @primary-color;
transition: border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
content: '';
}
.ant-menu-item-selected {
//background-color: red;
background-color: lighten(@primary-color, 53%);
/*.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:after{
border-bottom: 5px solid red;
background-color: red;
}*/
/*& > .ant-menu-item::after {
position: absolute;
right: 20px;
bottom: 0;
left: 20px;
border-bottom: 2px solid #0052d9;
transition: border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
content: '';
}*/
}
.ant-menu-item {
margin-left: 12px;
}
/*.ant-menu-item:not(.ant-menu-item-disabled):not(.ant-menu-item-selected):hover,
.ant-menu-submenu .ant-menu-submenu-title:not(.ant-menu-item-disabled):hover
{
border-radius:unset !important;
}*/
//display: inline-flex;
//justify-content: center;
}
.ant-menu-item, .ant-menu-submenu-title {
display: flex;
align-items: center;
& .ant-menu-item-icon {
margin-right: unset !important;
}
}
/** start style for horizontal menu */
.ant-menu-horizontal {
padding: 0 !important;
li {
padding: 5px;
}
}
/** end style for horizontal menu */
&-side-menu {
.ant-menu-item:not(.ant-menu-item-disabled):not(.ant-menu-item-selected):hover,
.ant-menu-submenu .ant-menu-submenu-title:not(.ant-menu-item-disabled):hover {
transition: background-color 0.9ms linear;
border-radius: var(--border-radius-base);
color: unset !important;
//color: red;
background-color: #f3f3f3 !important;
}
}
/* .ant-menu-inline-collapsed{
.ant-menu-item,.ant-menu-submenu-title{
//display: block;
}
}*/
.ant-menu {
padding: 10px;
user-select: none;
}
.ant-menu-title-content {
font-size: 15px;
}
.ant-menu-item, .ant-menu-submenu-title {
//padding: unset !important;
@apply px-2 !important;
}
.ant-menu.ant-menu-inline-collapsed > .ant-menu-item .ant-menu-item-icon + span,
.ant-menu.ant-menu-inline-collapsed > .ant-menu-submenu .ant-menu-item-icon + span {
display: none !important;
/*display: flex !important;
flex-direction: column !important;
justify-content: center !important;*/
}
.ant-menu.ant-menu-inline-collapsed > .ant-menu-submenu > .ant-menu-submenu-title {
padding: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
//,.ant-menu.ant-menu-inline-collapsed > .ant-menu-submenu
.ant-menu.ant-menu-inline-collapsed > .ant-menu-item {
padding: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
.ant-menu-vertical > .ant-menu-submenu > .ant-menu-submenu-title {
height: 36px;
}
/*.ant-menu-submenu-title,.ant-menu-submenu-arrow{
//color: #f3f3f3 !important;
}*/
.ant-menu-item.ant-menu-item-selected {
//border-radius: var(--border-radius-base);
&::after {
content: unset;
}
}
.ant-menu.ant-menu-sub.ant-menu-inline {
background-color: unset !important;
}
.ant-menu-vertical > .ant-menu-submenu,
.ant-menu-vertical > .ant-menu-item {
height: 36px;
}
}
</style>
+120
View File
@@ -0,0 +1,120 @@
<template>
<!-- <div :class="[prefixCls]">-->
<ASubMenu :class="prefixCls" :popupClassName="`${prefixCls}-popup`" :popupOffset="popupOffset">
<template #icon>
<BaseIcon :size="menuIconSize" v-if="item.meta?.icon" :icon="item.meta.icon" />
</template>
<template #title>
<span>{{ item.meta.title ?? item.name }}</span>
</template>
<template v-for="childItem in menu">
<BasicSubMenu
v-if="!childItem.meta?.hideChildren && childItem.children && childItem.children.length > 0"
:menu-item="childItem"
:key="childItem.name"
:level="props.level + 1"
/>
<!-- <AMenuItem v-else :key="childItem.name" :disabled="childItem.meta.disabled ?? false">
<template #icon>
<BaseIcon :size="menuIconSize" v-if="childItem.meta.icon" :icon="childItem.meta.icon" />
</template>
<span>{{ childItem.meta.title ?? childItem.name }}</span>
</AMenuItem>-->
<BaseMenuItem v-else :item="childItem" :key="childItem.name" />
</template>
</ASubMenu>
</template>
<script lang="ts" setup>
import { usePrefix } from '@core/composable/usePrefix'
import { menuIconSize } from '@root/config'
import { computed } from 'vue'
import { sortBy } from 'lodash'
//import { routes } from '@/core/router/routes'
import type { RouteRecordRaw } from 'vue-router'
import BaseMenuItem from '@core/layouts/composnts/BaseMenuItem.vue'
const { prefixCls } = usePrefix('basic-sub-menu')
interface Props {
menuItem: RouteRecordRaw
level: string | number
}
const props = withDefaults(defineProps<Props>(), {
level: 1
})
const popupOffset = computed(() => {
if (props.level == 1) return [-63, 3]
return [-8, -8]
})
const item = computed(() => {
return props.menuItem
})
const menu = computed(() => {
return sortBy(props.menuItem.children, (item) => {
return item.meta?.order
}).filter((item) => !item.meta?.hideMenu)
})
</script>
<style lang="less">
@prefix:~ '@{prefixCls}-basic-sub-menu';
.@{prefix}{
&.ant-menu-submenu-selected{
border-radius: var(--border-radius-base);
background-color: var(--ant-primary-1);
.ant-menu-submenu-arrow{
color: var(--ant-primary)
}
}
&-popup{
/*padding-right: 20px;*/
ul{
//display: none;
border-radius: var(--border-radius-base);
padding: 5px !important;
}
.ant-menu-vertical.ant-menu-sub > .ant-menu-item{
display: flex !important;
align-items: center;
gap:5px;
}
//background-color: red;
.ant-menu-vertical > .ant-menu-submenu > .ant-menu-submenu-title{
display: flex;
padding: 0 5px;
gap:5px;
align-items: center;
height: 36px;
}
li.ant-menu-item{
border-radius: var(--border-radius-base);
padding:0 5px;
}
.ant-menu-vertical > .ant-menu-item{
height: 36px;
}
/* .ant-menu-submenu.ant-menu-submenu-popup{
}*/
.ant-menu-submenu-arrow{
right: unset !important;
left: 10px;
transform: rotate(180deg);
}
}
}
</style>