You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
6.8 KiB
Vue
265 lines
6.8 KiB
Vue
<template>
|
|
<view class="nav-section">
|
|
<view class="section-header">
|
|
<text class="section-title">{{ t('dashboard.functionNav') }}</text>
|
|
<view class="more-btn" @click="handleMoreClick">
|
|
<text class="more-text">{{ t('common.more') }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="nav-grid">
|
|
<view v-for="(item, index) in displayNavList" :key="item.id || index" class="nav-item" @click="handleNavClick(item)">
|
|
<view class="nav-icon" :style="{ background: '#e1e6eb' }">
|
|
<uni-icons v-if="isUniIcon(item.icon)" :type="getUniIconName(item.icon)" size="28" :color="item.accentColor" />
|
|
<u-icon v-else-if="isUviewIcon(item.icon)" :name="getUviewIconName(item.icon)" size="28" :color="item.accentColor"></u-icon>
|
|
<text v-else class="nav-icon-text">{{ item.symbol }}</text>
|
|
</view>
|
|
<text class="nav-text">{{ item.displayName }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<NavMenuMore
|
|
:visible="showMoreModal"
|
|
:menu-list="moreNavList"
|
|
@close="showMoreModal = false"
|
|
@edit="handleEdit"
|
|
/>
|
|
<NavMenuEditor
|
|
:visible="showEditModal"
|
|
@close="showEditModal = false"
|
|
@update="handleUpdate"
|
|
/>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { getUserNavMenuList } from '@/api/system/userNavMenu'
|
|
import { getNavPermissionInfo } from '@/api/login'
|
|
import { buildNavMenuViewModels } from '@/utils/permissionMenu'
|
|
import NavMenuMore from './NavMenuMore.vue'
|
|
import NavMenuEditor from './NavMenuEditor.vue'
|
|
|
|
const { t } = useI18n()
|
|
const TABBAR_VISIBILITY_EVENT = 'tabbar-visibility-change'
|
|
|
|
function hexToRgba(hex, alpha) {
|
|
const value = String(hex || '').replace('#', '')
|
|
if (value.length !== 6) {
|
|
return `rgba(45, 90, 135, ${alpha})`
|
|
}
|
|
const red = parseInt(value.slice(0, 2), 16)
|
|
const green = parseInt(value.slice(2, 4), 16)
|
|
const blue = parseInt(value.slice(4, 6), 16)
|
|
return `rgba(${red}, ${green}, ${blue}, ${alpha})`
|
|
}
|
|
|
|
function isUniIcon(icon) {
|
|
return String(icon || '').startsWith('uni-icons:')
|
|
}
|
|
|
|
function isUviewIcon(icon) {
|
|
return String(icon || '').startsWith('uview-plus:')
|
|
}
|
|
|
|
function getUniIconName(icon) {
|
|
return String(icon || '').replace(/^uni-icons:/, '').trim()
|
|
}
|
|
|
|
function getUviewIconName(icon) {
|
|
return String(icon || '').replace(/^uview-plus:/, '').trim()
|
|
}
|
|
|
|
const showMoreModal = ref(false)
|
|
const showEditModal = ref(false)
|
|
const configuredMenuList = ref([])
|
|
const allMenuList = ref([])
|
|
const loaded = ref(false)
|
|
|
|
const defaultNavList = [
|
|
{ id: 'default-mold', displayName: '模具', symbol: '🔧', accentColor: '#1a3a5c', route: '/pages_function/pages/mold/index' },
|
|
{ id: 'default-equipment', displayName: '设备', symbol: '⚙️', accentColor: '#2d5a87', route: '/pages_function/pages/equipment/index' },
|
|
{ id: 'default-keypart', displayName: '配件', symbol: '🔩', accentColor: '#3d7ab5', route: '/pages_function/pages/keypart/index' },
|
|
{ id: 'default-spare', displayName: '备件', symbol: '📦', accentColor: '#4a90c2', route: '/pages_function/pages/spare/index' },
|
|
{ id: 'default-product', displayName: '产品', symbol: '🧾', accentColor: '#5aa0d2', route: '/pages_function/pages/product/index' }
|
|
]
|
|
|
|
const displayNavList = computed(() => {
|
|
if (!loaded.value) {
|
|
return []
|
|
}
|
|
if (configuredMenuList.value.length === 0) {
|
|
return defaultNavList
|
|
}
|
|
return configuredMenuList.value.slice(0, 5)
|
|
})
|
|
|
|
const moreNavList = computed(() => {
|
|
if (!loaded.value) {
|
|
return []
|
|
}
|
|
if (configuredMenuList.value.length === 0) {
|
|
return defaultNavList
|
|
}
|
|
return configuredMenuList.value
|
|
})
|
|
|
|
const modalVisible = computed(() => showMoreModal.value || showEditModal.value)
|
|
let tabbarRestoreTimer = null
|
|
|
|
async function loadConfiguredMenus() {
|
|
try {
|
|
const menuRes = await getNavPermissionInfo()
|
|
allMenuList.value = buildNavMenuViewModels(menuRes?.data?.menus)
|
|
|
|
const navRes = await getUserNavMenuList()
|
|
const configuredRecords = Array.isArray(navRes?.data) ? [...navRes.data] : []
|
|
configuredRecords.sort((left, right) => Number(left?.sort || 0) - Number(right?.sort || 0))
|
|
const configuredIds = configuredRecords.map(item => item.menuId)
|
|
|
|
if (configuredIds.length === 0) {
|
|
configuredMenuList.value = []
|
|
loaded.value = true
|
|
return
|
|
}
|
|
|
|
const configuredMenus = allMenuList.value
|
|
.filter(item => configuredIds.includes(item.id))
|
|
.sort((a, b) => {
|
|
const indexA = configuredIds.indexOf(a.id)
|
|
const indexB = configuredIds.indexOf(b.id)
|
|
return indexA - indexB
|
|
})
|
|
|
|
configuredMenuList.value = configuredMenus
|
|
loaded.value = true
|
|
} catch (error) {
|
|
console.error('加载配置菜单失败:', error)
|
|
configuredMenuList.value = []
|
|
loaded.value = true
|
|
}
|
|
}
|
|
|
|
function handleNavClick(item) {
|
|
if (item.route) {
|
|
uni.navigateTo({ url: item.route })
|
|
} else {
|
|
uni.showToast({ title: t('common.moduleBuilding'), icon: 'none' })
|
|
}
|
|
}
|
|
|
|
function handleMoreClick() {
|
|
showMoreModal.value = true
|
|
}
|
|
|
|
function handleEdit() {
|
|
showMoreModal.value = false
|
|
showEditModal.value = true
|
|
}
|
|
|
|
function handleUpdate() {
|
|
loadConfiguredMenus()
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadConfiguredMenus()
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
if (tabbarRestoreTimer) {
|
|
clearTimeout(tabbarRestoreTimer)
|
|
tabbarRestoreTimer = null
|
|
}
|
|
uni.$emit(TABBAR_VISIBILITY_EVENT, true)
|
|
})
|
|
|
|
watch(modalVisible, (visible) => {
|
|
if (tabbarRestoreTimer) {
|
|
clearTimeout(tabbarRestoreTimer)
|
|
tabbarRestoreTimer = null
|
|
}
|
|
|
|
if (visible) {
|
|
uni.$emit(TABBAR_VISIBILITY_EVENT, false)
|
|
return
|
|
}
|
|
|
|
tabbarRestoreTimer = setTimeout(() => {
|
|
uni.$emit(TABBAR_VISIBILITY_EVENT, true)
|
|
tabbarRestoreTimer = null
|
|
}, 300)
|
|
}, { immediate: true })
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.nav-section {
|
|
background: #ffffff;
|
|
border-radius: 20rpx;
|
|
padding: 28rpx;
|
|
margin-bottom: 24rpx;
|
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 32rpx;
|
|
font-weight: 600;
|
|
color: #1a3a5c;
|
|
}
|
|
|
|
.more-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6rpx;
|
|
padding: 8rpx 16rpx;
|
|
border-radius: 16rpx;
|
|
background: rgba(34, 72, 110, 0.08);
|
|
}
|
|
|
|
.more-text {
|
|
font-size: 26rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.nav-grid {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
}
|
|
|
|
.nav-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
|
|
&:active {
|
|
opacity: 0.7;
|
|
}
|
|
}
|
|
|
|
.nav-icon {
|
|
width: 96rpx;
|
|
height: 96rpx;
|
|
border-radius: 24rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 16rpx;
|
|
|
|
.nav-icon-text {
|
|
font-size: 32rpx;
|
|
color: #1a3a5c;
|
|
font-weight: 700;
|
|
}
|
|
}
|
|
|
|
.nav-text {
|
|
font-size: 26rpx;
|
|
color: #333333;
|
|
}
|
|
</style> |