style:功能导航模块交互优化

master
黄伟杰 2 days ago
parent af78c2ffa3
commit 4bc2589ced

@ -1,7 +1,6 @@
<template> <template>
<view v-if="visible" class="nav-menu-editor-mask" @click="handleClose"> <view v-if="renderVisible" class="nav-menu-editor-mask" :class="{ 'mask-hidden': !animVisible }" @click="handleClose">
<view class="nav-menu-editor" @click.stop @touchmove.stop.prevent="handleDragMove" @touchend.stop="handleDragEnd" <view class="nav-menu-editor" :class="{ 'panel-hidden': !animVisible }" @click.stop>
@touchcancel.stop="handleDragEnd">
<view class="editor-header"> <view class="editor-header">
<text class="editor-title">{{ t('dashboard.editNavMenu') }}</text> <text class="editor-title">{{ t('dashboard.editNavMenu') }}</text>
<view class="editor-close" @click="handleClose"> <view class="editor-close" @click="handleClose">
@ -12,19 +11,17 @@
<view class="editor-content"> <view class="editor-content">
<view class="nav-section"> <view class="nav-section">
<text class="nav-section-title">{{ t('dashboard.configuredNav') }}</text> <text class="nav-section-title">{{ t('dashboard.configuredNav') }}</text>
<view id="configured-zone" class="nav-grid" :class="{ 'nav-grid-target': isSectionDropTarget('configured') }"> <view class="nav-grid">
<view v-for="item in configuredMenuList" :key="item.id" :id="`configured-item-${item.id}`" class="nav-item" <view v-for="item in configuredMenuList" :key="item.id" class="nav-item is-selected">
:class="{ <view class="nav-icon" :style="{ background: '#e1e6eb' }">
'is-drop-target': isItemDropTarget('configured', item.id), <uni-icons v-if="isUniIcon(item.icon)" :type="getUniIconName(item.icon)" size="28" :color="item.accentColor" />
'is-dragging-source': draggingMenu?.id === item.id <u-icon v-else-if="isUviewIcon(item.icon)" :name="getUviewIconName(item.icon)" size="28" :color="item.accentColor"></u-icon>
}" @click="handleConfiguredClick(item)"
@longpress.stop.prevent="handleItemLongPress(item, 'configured', $event)">
<view class="nav-icon" :style="{ background: hexToRgba(item.accentColor, 0.1) }">
<uni-icons v-if="isUniIcon(item.icon)" :type="getUniIconName(item.icon)" size="24" :color="item.accentColor" />
<u-icon v-else-if="isUviewIcon(item.icon)" :name="getUviewIconName(item.icon)" size="24" :color="item.accentColor"></u-icon>
<text v-else class="nav-icon-text">{{ item.symbol }}</text> <text v-else class="nav-icon-text">{{ item.symbol }}</text>
</view> </view>
<text class="nav-text">{{ item.displayName }}</text> <text class="nav-text">{{ item.displayName }}</text>
<view class="check-badge" @click.stop="handleConfiguredClick(item)">
<u-icon name="minus-circle-fill" size="18" color="#de3730"></u-icon>
</view>
</view> </view>
</view> </view>
</view> </view>
@ -33,17 +30,12 @@
<view class="nav-section"> <view class="nav-section">
<text class="nav-section-title">{{ t('dashboard.unconfiguredNav') }}</text> <text class="nav-section-title">{{ t('dashboard.unconfiguredNav') }}</text>
<view id="unconfigured-zone" class="nav-grid" <view class="nav-grid">
:class="{ 'nav-grid-target': isSectionDropTarget('unconfigured') }"> <view v-for="item in unconfiguredMenuList" :key="item.id" class="nav-item"
<view v-for="item in unconfiguredMenuList" :key="item.id" :id="`unconfigured-item-${item.id}`" @click="handleUnconfiguredClick(item)">
class="nav-item nav-item-disabled" :class="{ <view class="nav-icon nav-icon-disabled" :style="{ background: '#e1e6eb' }">
'is-drop-target': isItemDropTarget('unconfigured', item.id), <uni-icons v-if="isUniIcon(item.icon)" :type="getUniIconName(item.icon)" size="28" :color="item.accentColor" />
'is-dragging-source': draggingMenu?.id === item.id <u-icon v-else-if="isUviewIcon(item.icon)" :name="getUviewIconName(item.icon)" size="28" :color="item.accentColor"></u-icon>
}" @click="handleUnconfiguredClick(item)"
@longpress.stop.prevent="handleItemLongPress(item, 'unconfigured', $event)">
<view class="nav-icon nav-icon-disabled" :style="{ background: hexToRgba(item.accentColor, 0.1) }">
<uni-icons v-if="isUniIcon(item.icon)" :type="getUniIconName(item.icon)" size="24" :color="item.accentColor" />
<u-icon v-else-if="isUviewIcon(item.icon)" :name="getUviewIconName(item.icon)" size="24" :color="item.accentColor"></u-icon>
<text v-else class="nav-icon-text">{{ item.symbol }}</text> <text v-else class="nav-icon-text">{{ item.symbol }}</text>
</view> </view>
<text class="nav-text nav-text-disabled">{{ item.displayName }}</text> <text class="nav-text nav-text-disabled">{{ item.displayName }}</text>
@ -51,8 +43,7 @@
</view> </view>
</view> </view>
<text class="click-hint">{{ t('dashboard.dragHint') }}</text> <text class="click-hint">{{ t('dashboard.clickHint') }}</text>
<text class="click-hint secondary-hint">{{ t('dashboard.clickHint') }}</text>
</view> </view>
<view class="editor-footer"> <view class="editor-footer">
@ -63,21 +54,12 @@
<text>{{ t('common.complete') }}</text> <text>{{ t('common.complete') }}</text>
</view> </view>
</view> </view>
<view v-if="draggingMenu" class="drag-ghost" :style="dragGhostStyle">
<view class="nav-icon" :style="{ background: hexToRgba(draggingMenu.accentColor, 0.1) }">
<uni-icons v-if="isUniIcon(draggingMenu.icon)" :type="getUniIconName(draggingMenu.icon)" size="24" :color="draggingMenu.accentColor" />
<u-icon v-else-if="isUviewIcon(draggingMenu.icon)" :name="getUviewIconName(draggingMenu.icon)" size="24" :color="draggingMenu.accentColor"></u-icon>
<text v-else class="nav-icon-text">{{ draggingMenu.symbol }}</text>
</view>
<text class="nav-text">{{ draggingMenu.displayName }}</text>
</view>
</view> </view>
</view> </view>
</template> </template>
<script setup> <script setup>
import { ref, computed, watch, nextTick, getCurrentInstance } from 'vue' import { ref, computed, watch, nextTick } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { getUserNavMenuList, updateUserNavMenuList } from '@/api/system/userNavMenu' import { getUserNavMenuList, updateUserNavMenuList } from '@/api/system/userNavMenu'
@ -86,18 +68,6 @@ import { buildNavMenuViewModels } from '@/utils/permissionMenu'
const { t } = useI18n() const { t } = useI18n()
const userStore = useUserStore() const userStore = useUserStore()
const instance = getCurrentInstance()
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) { function isUniIcon(icon) {
return String(icon || '').startsWith('uni-icons:') return String(icon || '').startsWith('uni-icons:')
@ -124,14 +94,25 @@ const props = defineProps({
const emit = defineEmits(['close', 'update']) const emit = defineEmits(['close', 'update'])
const renderVisible = ref(false)
const animVisible = ref(false)
watch(() => props.visible, (val) => {
if (val) {
renderVisible.value = true
nextTick(() => {
setTimeout(() => { animVisible.value = true }, 30)
})
} else {
animVisible.value = false
setTimeout(() => { renderVisible.value = false }, 300)
}
}, { immediate: true })
const userMenuList = ref([]) const userMenuList = ref([])
const configuredRecords = ref([]) const configuredRecords = ref([])
const originConfIds = ref([]) const originConfIds = ref([])
const configuredIds = ref([]) const configuredIds = ref([])
const dragTargets = ref([])
const dropTarget = ref(null)
const draggingState = ref(null)
const lastDragEndAt = ref(0)
const menuMap = computed(() => { const menuMap = computed(() => {
const map = {} const map = {}
@ -141,24 +122,6 @@ const menuMap = computed(() => {
return map return map
}) })
const draggingMenu = computed(() => {
if (!draggingState.value) {
return null
}
return menuMap.value[draggingState.value.id] || null
})
const dragGhostStyle = computed(() => {
if (!draggingState.value) {
return {}
}
return {
left: `${draggingState.value.x - 48}px`,
top: `${draggingState.value.y - 48}px`
}
})
const configuredMenuList = computed(() => { const configuredMenuList = computed(() => {
return configuredIds.value return configuredIds.value
.map(id => menuMap.value[id]) .map(id => menuMap.value[id])
@ -183,24 +146,16 @@ async function loadData() {
.filter(menuId => !!menuMap.value[menuId]) .filter(menuId => !!menuMap.value[menuId])
originConfIds.value = [...configuredIds.value] originConfIds.value = [...configuredIds.value]
await nextTick()
collectDropTargets()
} catch (error) { } catch (error) {
console.error('加载菜单数据失败:', error) console.error('加载菜单数据失败:', error)
} }
} }
function handleConfiguredClick(item) { function handleConfiguredClick(item) {
if (shouldIgnoreClick()) {
return
}
configuredIds.value = configuredIds.value.filter(id => id !== item.id) configuredIds.value = configuredIds.value.filter(id => id !== item.id)
} }
function handleUnconfiguredClick(item) { function handleUnconfiguredClick(item) {
if (shouldIgnoreClick()) {
return
}
if (!configuredIds.value.includes(item.id)) { if (!configuredIds.value.includes(item.id)) {
configuredIds.value.push(item.id) configuredIds.value.push(item.id)
} }
@ -232,217 +187,13 @@ async function handleConfirm() {
} }
function handleClose() { function handleClose() {
resetDragState()
emit('close') emit('close')
} }
function shouldIgnoreClick() {
return Date.now() - lastDragEndAt.value < 300
}
function getTouchPoint(event) {
const touch = event?.changedTouches?.[0] || event?.touches?.[0]
if (touch) {
return {
x: touch.clientX ?? touch.pageX ?? 0,
y: touch.clientY ?? touch.pageY ?? 0
}
}
if (typeof event?.detail?.x === 'number' && typeof event?.detail?.y === 'number') {
return {
x: event.detail.x,
y: event.detail.y
}
}
return null
}
function collectDropTargets() {
if (!instance?.proxy) {
return
}
const selectors = [
{ selector: '#configured-zone', kind: 'section', section: 'configured' },
{ selector: '#unconfigured-zone', kind: 'section', section: 'unconfigured' }
]
configuredMenuList.value.forEach((item) => {
selectors.push({
selector: `#configured-item-${item.id}`,
kind: 'item',
section: 'configured',
id: item.id
})
})
unconfiguredMenuList.value.forEach((item) => {
selectors.push({
selector: `#unconfigured-item-${item.id}`,
kind: 'item',
section: 'unconfigured',
id: item.id
})
})
const query = uni.createSelectorQuery().in(instance.proxy)
selectors.forEach((item) => {
query.select(item.selector).boundingClientRect()
})
query.exec((rects = []) => {
dragTargets.value = rects
.map((rect, index) => {
if (!rect) {
return null
}
return {
...selectors[index],
...rect
}
})
.filter(Boolean)
})
}
function updateDropTarget(point) {
const itemTarget = dragTargets.value.find((item) => {
return item.kind === 'item'
&& point.x >= item.left
&& point.x <= item.right
&& point.y >= item.top
&& point.y <= item.bottom
})
if (itemTarget) {
dropTarget.value = {
section: itemTarget.section,
id: itemTarget.id,
position: point.y < itemTarget.top + itemTarget.height / 2 ? 'before' : 'after'
}
return
}
const sectionTarget = dragTargets.value.find((item) => {
return item.kind === 'section'
&& point.x >= item.left
&& point.x <= item.right
&& point.y >= item.top
&& point.y <= item.bottom
})
dropTarget.value = sectionTarget
? { section: sectionTarget.section, id: null, position: 'end' }
: null
}
async function handleItemLongPress(item, section, event) {
const point = getTouchPoint(event) || { x: 0, y: 0 }
draggingState.value = {
id: item.id,
section,
x: point.x,
y: point.y
}
dropTarget.value = {
section,
id: item.id,
position: 'after'
}
await nextTick()
collectDropTargets()
updateDropTarget(point)
}
function handleDragMove(event) {
if (!draggingState.value) {
return
}
const point = getTouchPoint(event)
if (!point) {
return
}
draggingState.value = {
...draggingState.value,
x: point.x,
y: point.y
}
updateDropTarget(point)
}
function applyDrop() {
if (!draggingState.value || !dropTarget.value) {
return
}
if (dropTarget.value.section === 'unconfigured') {
configuredIds.value = configuredIds.value.filter(id => id !== draggingState.value.id)
return
}
if (dropTarget.value.section !== 'configured') {
return
}
const nextIds = configuredIds.value.filter(id => id !== draggingState.value.id)
let insertIndex = nextIds.length
if (dropTarget.value.id != null) {
const targetIndex = nextIds.indexOf(dropTarget.value.id)
if (targetIndex !== -1) {
insertIndex = dropTarget.value.position === 'before' ? targetIndex : targetIndex + 1
}
}
nextIds.splice(insertIndex, 0, draggingState.value.id)
configuredIds.value = nextIds
}
function handleDragEnd() {
if (!draggingState.value) {
return
}
applyDrop()
lastDragEndAt.value = Date.now()
resetDragState()
}
function resetDragState() {
dropTarget.value = null
draggingState.value = null
}
function isItemDropTarget(section, itemId) {
return dropTarget.value?.section === section && dropTarget.value?.id === itemId
}
function isSectionDropTarget(section) {
return dropTarget.value?.section === section && dropTarget.value?.id == null
}
watch(() => props.visible, async (val) => { watch(() => props.visible, async (val) => {
if (val) { if (val) {
await loadData() await loadData()
return
}
resetDragState()
})
watch([configuredMenuList, unconfiguredMenuList], async () => {
if (!props.visible) {
return
} }
await nextTick()
collectDropTargets()
}) })
</script> </script>
@ -459,15 +210,25 @@ watch([configuredMenuList, unconfiguredMenuList], async () => {
align-items: flex-end; align-items: flex-end;
padding-bottom: calc(100rpx + env(safe-area-inset-bottom)); padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
box-sizing: border-box; box-sizing: border-box;
transition: opacity 0.3s ease;
&.mask-hidden {
opacity: 0;
}
} }
.nav-menu-editor { .nav-menu-editor {
width: 100%; width: 100%;
max-height: 66.6vh; max-height: 75vh;
background: #ffffff; background: #ffffff;
border-radius: 32rpx 32rpx 0 0; border-radius: 32rpx 32rpx 0 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
transition: transform 0.3s ease;
&.panel-hidden {
transform: translateY(100%);
}
} }
.editor-header { .editor-header {
@ -516,11 +277,6 @@ watch([configuredMenuList, unconfiguredMenuList], async () => {
flex-wrap: wrap; flex-wrap: wrap;
min-height: 120rpx; min-height: 120rpx;
border-radius: 24rpx; border-radius: 24rpx;
transition: background-color 0.15s ease;
}
.nav-grid-target {
background: rgba(34, 72, 110, 0.06);
} }
.nav-item { .nav-item {
@ -531,23 +287,12 @@ watch([configuredMenuList, unconfiguredMenuList], async () => {
margin-bottom: 24rpx; margin-bottom: 24rpx;
padding: 16rpx; padding: 16rpx;
border-radius: 16rpx; border-radius: 16rpx;
position: relative;
transition: all 0.15s; transition: all 0.15s;
&:active { &:active {
opacity: 0.7; opacity: 0.7;
} }
&.nav-item-disabled {
opacity: 0.72;
}
}
.is-drop-target {
background: rgba(34, 72, 110, 0.08);
}
.is-dragging-source {
opacity: 0.25;
} }
.nav-icon { .nav-icon {
@ -559,12 +304,8 @@ watch([configuredMenuList, unconfiguredMenuList], async () => {
justify-content: center; justify-content: center;
margin-bottom: 12rpx; margin-bottom: 12rpx;
&.nav-icon-disabled {
opacity: 0.55;
}
.nav-icon-text { .nav-icon-text {
font-size: 28rpx; font-size: 32rpx;
color: #1a3a5c; color: #1a3a5c;
font-weight: 700; font-weight: 700;
} }
@ -580,15 +321,24 @@ watch([configuredMenuList, unconfiguredMenuList], async () => {
} }
} }
.check-badge {
position: absolute;
top: 8rpx;
right: 26rpx;
width: 32rpx;
height: 32rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.click-hint { .click-hint {
font-size: 24rpx; font-size: 24rpx;
color: #999; color: #999;
text-align: center; text-align: center;
display: block; display: block;
margin-top: 16rpx; margin-top: 16rpx;
}
.secondary-hint {
padding-bottom: 24rpx; padding-bottom: 24rpx;
} }
@ -620,15 +370,4 @@ watch([configuredMenuList, unconfiguredMenuList], async () => {
background: #22486e; background: #22486e;
color: #ffffff; color: #ffffff;
} }
</style>
.drag-ghost {
position: fixed;
z-index: 1002;
width: 96rpx;
margin-left: -8rpx;
pointer-events: none;
display: flex;
flex-direction: column;
align-items: center;
}
</style>

@ -1,6 +1,6 @@
<template> <template>
<view v-if="visible" class="nav-menu-more-mask" @click="handleClose"> <view v-if="renderVisible" class="nav-menu-more-mask" :class="{ 'mask-hidden': !animVisible }" @click="handleClose">
<view class="nav-menu-more" @click.stop> <view class="nav-menu-more" :class="{ 'panel-hidden': !animVisible }" @click.stop>
<view class="more-header"> <view class="more-header">
<text class="more-title">{{ t('dashboard.allNavMenu') }}</text> <text class="more-title">{{ t('dashboard.allNavMenu') }}</text>
<view class="more-actions"> <view class="more-actions">
@ -21,9 +21,9 @@
class="nav-item" class="nav-item"
@click="handleNavClick(item)" @click="handleNavClick(item)"
> >
<view class="nav-icon" :style="{ background: hexToRgba(item.accentColor, 0.1) }"> <view class="nav-icon" :style="{ background: '#e1e6eb' }">
<uni-icons v-if="isUniIcon(item.icon)" :type="getUniIconName(item.icon)" size="24" :color="item.accentColor" /> <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="24" :color="item.accentColor"></u-icon> <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> <text v-else class="nav-icon-text">{{ item.symbol }}</text>
</view> </view>
<text class="nav-text">{{ item.displayName }}</text> <text class="nav-text">{{ item.displayName }}</text>
@ -35,7 +35,7 @@
</template> </template>
<script setup> <script setup>
import { computed } from 'vue' import { computed, ref, watch, nextTick } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
const { t } = useI18n() const { t } = useI18n()
@ -80,6 +80,21 @@ const props = defineProps({
const emit = defineEmits(['close', 'edit']) const emit = defineEmits(['close', 'edit'])
const renderVisible = ref(false)
const animVisible = ref(false)
watch(() => props.visible, (val) => {
if (val) {
renderVisible.value = true
nextTick(() => {
setTimeout(() => { animVisible.value = true }, 30)
})
} else {
animVisible.value = false
setTimeout(() => { renderVisible.value = false }, 300)
}
}, { immediate: true })
const displayMenuList = computed(() => Array.isArray(props.menuList) ? props.menuList : []) const displayMenuList = computed(() => Array.isArray(props.menuList) ? props.menuList : [])
function handleNavClick(item) { function handleNavClick(item) {
@ -112,6 +127,11 @@ function handleClose() {
align-items: flex-end; align-items: flex-end;
padding-bottom: calc(100rpx + env(safe-area-inset-bottom)); padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
box-sizing: border-box; box-sizing: border-box;
transition: opacity 0.3s ease;
&.mask-hidden {
opacity: 0;
}
} }
.nav-menu-more { .nav-menu-more {
@ -121,6 +141,11 @@ function handleClose() {
border-radius: 32rpx 32rpx 0 0; border-radius: 32rpx 32rpx 0 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
transition: transform 0.3s ease;
&.panel-hidden {
transform: translateY(100%);
}
} }
.more-header { .more-header {
@ -189,7 +214,7 @@ function handleClose() {
} }
.nav-item { .nav-item {
width: 20%; width: 25%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -211,7 +236,7 @@ function handleClose() {
margin-bottom: 16rpx; margin-bottom: 16rpx;
.nav-icon-text { .nav-icon-text {
font-size: 28rpx; font-size: 32rpx;
color: #1a3a5c; color: #1a3a5c;
font-weight: 700; font-weight: 700;
} }

@ -4,14 +4,13 @@
<text class="section-title">{{ t('dashboard.functionNav') }}</text> <text class="section-title">{{ t('dashboard.functionNav') }}</text>
<view class="more-btn" @click="handleMoreClick"> <view class="more-btn" @click="handleMoreClick">
<text class="more-text">{{ t('common.more') }}</text> <text class="more-text">{{ t('common.more') }}</text>
<uni-icons type="arrow-right" size="18" color="#999"></uni-icons>
</view> </view>
</view> </view>
<view class="nav-grid"> <view class="nav-grid">
<view v-for="(item, index) in displayNavList" :key="item.id || index" class="nav-item" @click="handleNavClick(item)"> <view v-for="(item, index) in displayNavList" :key="item.id || index" class="nav-item" @click="handleNavClick(item)">
<view class="nav-icon" :style="{ background: hexToRgba(item.accentColor, 0.1) }"> <view class="nav-icon" :style="{ background: '#e1e6eb' }">
<uni-icons v-if="isUniIcon(item.icon)" :type="getUniIconName(item.icon)" size="24" :color="item.accentColor" /> <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="24" :color="item.accentColor"></u-icon> <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> <text v-else class="nav-icon-text">{{ item.symbol }}</text>
</view> </view>
<text class="nav-text">{{ item.displayName }}</text> <text class="nav-text">{{ item.displayName }}</text>
@ -74,6 +73,7 @@ const showMoreModal = ref(false)
const showEditModal = ref(false) const showEditModal = ref(false)
const configuredMenuList = ref([]) const configuredMenuList = ref([])
const allMenuList = ref([]) const allMenuList = ref([])
const loaded = ref(false)
const defaultNavList = [ const defaultNavList = [
{ id: 'default-mold', displayName: '模具', symbol: '🔧', accentColor: '#1a3a5c', route: '/pages_function/pages/mold/index' }, { id: 'default-mold', displayName: '模具', symbol: '🔧', accentColor: '#1a3a5c', route: '/pages_function/pages/mold/index' },
@ -84,6 +84,9 @@ const defaultNavList = [
] ]
const displayNavList = computed(() => { const displayNavList = computed(() => {
if (!loaded.value) {
return []
}
if (configuredMenuList.value.length === 0) { if (configuredMenuList.value.length === 0) {
return defaultNavList return defaultNavList
} }
@ -91,6 +94,9 @@ const displayNavList = computed(() => {
}) })
const moreNavList = computed(() => { const moreNavList = computed(() => {
if (!loaded.value) {
return []
}
if (configuredMenuList.value.length === 0) { if (configuredMenuList.value.length === 0) {
return defaultNavList return defaultNavList
} }
@ -109,6 +115,7 @@ async function loadConfiguredMenus() {
if (configuredIds.length === 0) { if (configuredIds.length === 0) {
configuredMenuList.value = [] configuredMenuList.value = []
loaded.value = true
return return
} }
@ -121,9 +128,11 @@ async function loadConfiguredMenus() {
}) })
configuredMenuList.value = configuredMenus configuredMenuList.value = configuredMenus
loaded.value = true
} catch (error) { } catch (error) {
console.error('加载配置菜单失败:', error) console.error('加载配置菜单失败:', error)
configuredMenuList.value = [] configuredMenuList.value = []
loaded.value = true
} }
} }
@ -214,7 +223,7 @@ onMounted(() => {
margin-bottom: 16rpx; margin-bottom: 16rpx;
.nav-icon-text { .nav-icon-text {
font-size: 28rpx; font-size: 32rpx;
color: #1a3a5c; color: #1a3a5c;
font-weight: 700; font-weight: 700;
} }

Loading…
Cancel
Save