Compare commits

...

7 Commits

@ -0,0 +1 @@
VITE_APP_BASE_URL=http://192.168.5.106:48081

@ -0,0 +1 @@
VITE_APP_BASE_URL=https://besure.ngsk.tech:7001

@ -2,17 +2,17 @@
import { getToken } from '@/utils/auth'
import { initializeLocale, translateLiteral } from '@/locales'
import useUserStore from '@/store/modules/user'
import { applyNavigationBarTheme } from '@/utils/navigationBar'
let wrapped = false
function wrapUniTextApi() {
if (wrapped) return
wrapped = true
const rawShowToast = uni.showToast
const rawShowModal = uni.showModal
const rawShowLoading = uni.showLoading
const rawSetNavigationBarTitle = uni.setNavigationBarTitle
uni.showToast = (options = {}) => {
const next = { ...options }
if (typeof next.title === 'string') {
@ -43,22 +43,12 @@ function wrapUniTextApi() {
}
return rawShowModal(next)
}
uni.setNavigationBarTitle = (options = {}) => {
const next = { ...options }
if (typeof next.title === 'string') {
next.title = translateLiteral(next.title)
}
const result = rawSetNavigationBarTitle(next)
applyNavigationBarTheme()
return result
}
}
export default {
onLaunch: function () {
initializeLocale()
wrapUniTextApi()
applyNavigationBarTheme()
if (getToken()) {
useUserStore().getInfo().catch(() => {})
}
@ -66,7 +56,6 @@ export default {
onShow: function () {
initializeLocale()
wrapUniTextApi()
applyNavigationBarTheme()
},
onHide: function () {
}

@ -33,17 +33,27 @@ export function register(data) {
})
}
// 获取用户详细信息
export function getInfo() {
function getPermissionInfo(params = {}) {
return request({
url: '/admin-api/system/auth/get-permission-info',
method: 'get',
params: {
clientType: 2
clientType: 2,
...params
}
})
}
// 获取用户详细信息
export function getInfo() {
return getPermissionInfo()
}
// 获取功能导航菜单
export function getNavPermissionInfo() {
return getPermissionInfo()
}
// 退出方法
export function logout() {
return request({

@ -0,0 +1,16 @@
import request from '@/utils/request'
export function getUserNavMenuList() {
return request({
url: '/admin-api/system/user-nav-menu/list',
method: 'get'
})
}
export function updateUserNavMenuList(data) {
return request({
url: '/admin-api/system/user-nav-menu/update-list',
method: 'put',
data
})
}

@ -1,108 +0,0 @@
<template>
<view>
<view class="header-section">
<view class="header-main">
<view class="back-btn" @click="handleBack">
<text class="back-icon"></text>
</view>
<view class="header-title-wrap">
<text class="header-title">{{ translatedTitle }}</text>
</view>
<view class="header-placeholder"></view>
</view>
</view>
<view v-if="showSubTitle && subTitle" class="header-subtitle-wrap">
<text class="header-desc">{{ translatedSubTitle }}</text>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue'
import { translateLiteral } from '@/locales'
const props = defineProps({
title: {
type: String,
default: ''
},
subTitle: {
type: String,
default: ''
},
showSubTitle: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['back'])
const translatedTitle = computed(() => translateLiteral(props.title))
const translatedSubTitle = computed(() => translateLiteral(props.subTitle))
function handleBack() {
emit('back')
uni.navigateBack()
}
</script>
<style lang="scss" scoped>
.header-section {
--status-top: var(--status-bar-height, 0px);
background: linear-gradient(135deg, #1a3a5c 0%, #2d5a87 100%);
padding: calc(14rpx + var(--status-top)) 24rpx 20rpx;
position: relative;
}
.header-main {
display: flex;
align-items: center;
min-height: 72rpx;
}
.back-btn {
width: 72rpx;
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
}
.back-icon {
font-size: 56rpx;
line-height: 1;
color: #ffffff;
font-weight: 500;
}
.header-title-wrap {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.header-title {
font-size: 34rpx;
font-weight: 700;
color: #ffffff;
}
.header-placeholder {
width: 72rpx;
height: 72rpx;
}
.header-subtitle-wrap {
margin-top: 14rpx;
padding-left: 16rpx;
}
.header-desc {
display: block;
font-size: 34rpx;
font-weight: 700;
color: #1a3a5c;
}
</style>

@ -51,7 +51,7 @@ function handleClick(btn, index) {
background: #ffffff;
box-shadow: 0 -6rpx 24rpx rgba(0, 0, 0, 0.08);
padding: 20rpx 24rpx calc(20rpx + env(safe-area-inset-bottom));
z-index: 99;
z-index: 90;
}
.action-row {

@ -0,0 +1,119 @@
<template>
<view>
<up-navbar
:title="translatedTitle"
:bgColor="navbarBgColor"
:titleStyle="titleStyleObj"
:leftIcon="showBackBtn ? 'arrow-left' : ''"
:leftIconColor="navTextColor"
:leftIconSize="20"
:autoBack="false"
:placeholder="true"
:safeAreaInsetTop="true"
@leftClick="handleBack"
>
<template #right>
<slot name="right"></slot>
</template>
</up-navbar>
<view v-if="subTitle" class="navbar-subtitle-wrap">
<text class="navbar-subtitle">{{ translatedSubTitle }}</text>
</view>
</view>
</template>
<script setup>
import { computed, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import { translateLiteral } from '@/locales'
const { t } = useI18n()
const props = defineProps({
title: {
type: String,
default: ''
},
subTitle: {
type: String,
default: ''
},
backgroundColor: {
type: String,
default: '#22486e'
},
textColor: {
type: String,
default: '#ffffff'
},
showBack: {
type: Boolean,
default: undefined
}
})
const currentPagesLength = ref(1)
const isLoginPage = computed(() => {
const pages = getCurrentPages()
if (pages && pages.length > 0) {
return pages[pages.length - 1].route === 'pages/login'
}
return false
})
const showBackBtn = computed(() => {
if (props.showBack !== undefined) return props.showBack
return currentPagesLength.value > 1
})
const navbarBgColor = computed(() => {
return isLoginPage.value ? '#ffffff' : props.backgroundColor
})
const navTextColor = computed(() => {
return isLoginPage.value ? '#000000' : props.textColor
})
const translatedTitle = computed(() => translateLiteral(props.title))
const translatedSubTitle = computed(() => translateLiteral(props.subTitle))
const titleStyleObj = computed(() => {
return {
color: navTextColor.value,
fontWeight: '700',
fontSize: '34rpx'
}
})
onShow(() => {
const pages = getCurrentPages()
currentPagesLength.value = pages ? pages.length : 1
})
function handleBack() {
if (!showBackBtn.value) return
uni.navigateBack({
fail: () => {
uni.reLaunch({
url: '/pages/index'
})
}
})
}
</script>
<style lang="scss" scoped>
.navbar-subtitle-wrap {
padding: 14rpx 24rpx 20rpx;
background: #ffffff;
}
.navbar-subtitle {
font-size: 34rpx;
font-weight: 700;
color: #1a3a5c;
}
</style>

@ -85,9 +85,7 @@
<script setup>
import { computed, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import useUserStore from '@/store/modules/user'
import { applyTabBarLanguage } from '@/locales'
import { buildPageModules, findTabMenuByPage, getMenuSymbol, getModuleColor, resolveMenuUrl } from '@/utils/permissionMenu'
const props = defineProps({
@ -203,9 +201,7 @@ function handleClick(menu) {
})
}
onShow(() => {
applyTabBarLanguage()
})
</script>
<style lang="scss" scoped>

@ -0,0 +1,126 @@
<template>
<up-tabbar
:value="activeIndex"
:activeColor="activeColor"
:inactiveColor="inactiveColor"
:safeAreaInsetBottom="true"
:fixed="true"
:placeholder="true"
:border="false"
@change="handleChange"
zIndex="100"
>
<up-tabbar-item
v-for="(item, index) in tabList"
:key="index"
:text="item.text"
:name="index"
>
<template #active-icon>
<image :src="item.selectedIcon" class="tabbar-icon" mode="widthFix" />
</template>
<template #inactive-icon>
<image :src="item.icon" class="tabbar-icon" mode="widthFix" />
</template>
</up-tabbar-item>
</up-tabbar>
</template>
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import useUserStore from '@/store/modules/user'
import { storeToRefs } from 'pinia'
import { getDynamicTabMenus } from '@/utils/permissionMenu'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const userStore = useUserStore()
const { menus } = storeToRefs(userStore)
const inactiveColor = '#666666'
const activeColor = '#1a3a5c'
const activeIndex = ref(0)
const homeIcon = '/static/images/tabbar/home.png'
const homeSelectedIcon = '/static/images/tabbar/home_.png'
const reportIcon = '/static/images/tabbar/report.png'
const reportSelectedIcon = '/static/images/tabbar/report_.png'
const workIcon = '/static/images/tabbar/work.png'
const workSelectedIcon = '/static/images/tabbar/work_.png'
const mineIcon = '/static/images/tabbar/mine.png'
const mineSelectedIcon = '/static/images/tabbar/mine_.png'
const routeMap = {
'pages/index': 0,
'pages/report': 1,
'pages/work': 2,
'pages/mine': 3
}
function getCurrentActiveIndex() {
const pages = getCurrentPages()
if (pages && pages.length > 0) {
const route = pages[pages.length - 1].route
return routeMap[route] !== undefined ? routeMap[route] : 0
}
return 0
}
const tabList = computed(() => {
const dynamicMenus = getDynamicTabMenus(menus.value)
return [
{
text: t('nav.home'),
icon: homeIcon,
selectedIcon: homeSelectedIcon,
path: '/pages/index'
},
{
text: dynamicMenus[0]?.name || t('tab.report'),
icon: reportIcon,
selectedIcon: reportSelectedIcon,
path: '/pages/report'
},
{
text: dynamicMenus[1]?.name || dynamicMenus[0]?.name || t('tab.work'),
icon: workIcon,
selectedIcon: workSelectedIcon,
path: '/pages/work'
},
{
text: t('nav.mine'),
icon: mineIcon,
selectedIcon: mineSelectedIcon,
path: '/pages/mine'
}
]
})
onMounted(() => {
activeIndex.value = getCurrentActiveIndex()
})
function handleChange(index) {
if (activeIndex.value === index) return
activeIndex.value = index
const item = tabList.value[index]
if (item) {
uni.reLaunch({
url: item.path
})
}
}
watch(() => useUserStore().menus, () => {
activeIndex.value = getCurrentActiveIndex()
})
</script>
<style lang="scss" scoped>
.tabbar-icon {
width: 48rpx;
height: 48rpx;
}
</style>

@ -188,7 +188,7 @@ defineExpose({ loadTodoList })
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
z-index: 10;
}
.bell-icon {

@ -0,0 +1,377 @@
<template>
<view v-if="renderVisible" class="nav-menu-editor-mask" :class="{ 'mask-hidden': !animVisible }" @click="handleClose">
<view class="nav-menu-editor" :class="{ 'panel-hidden': !animVisible }" @click.stop>
<view class="editor-header">
<text class="editor-title">{{ t('dashboard.editNavMenu') }}</text>
<view class="editor-close" @click="handleClose">
<uni-icons type="close" size="24" color="#999"></uni-icons>
</view>
</view>
<view class="editor-content">
<view class="nav-section">
<text class="nav-section-title">{{ t('dashboard.configuredNav') }}</text>
<view class="nav-grid">
<view v-for="item in configuredMenuList" :key="item.id" class="nav-item is-selected">
<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 class="check-badge" @click.stop="handleConfiguredClick(item)">
<u-icon name="minus-circle-fill" size="18" color="#de3730"></u-icon>
</view>
</view>
</view>
</view>
<view class="divider-line"></view>
<view class="nav-section">
<text class="nav-section-title">{{ t('dashboard.unconfiguredNav') }}</text>
<view class="nav-grid">
<view v-for="item in unconfiguredMenuList" :key="item.id" class="nav-item"
@click="handleUnconfiguredClick(item)">
<view class="nav-icon nav-icon-disabled" :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 nav-text-disabled">{{ item.displayName }}</text>
</view>
</view>
</view>
<text class="click-hint">{{ t('dashboard.clickHint') }}</text>
</view>
<view class="editor-footer">
<view class="btn-reset" @click="handleReset">
<text>{{ t('common.reset') }}</text>
</view>
<view class="btn-confirm" @click="handleConfirm">
<text>{{ t('common.complete') }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, watch, nextTick } from 'vue'
import { useI18n } from 'vue-i18n'
import useUserStore from '@/store/modules/user'
import { getUserNavMenuList, updateUserNavMenuList } from '@/api/system/userNavMenu'
import { getNavPermissionInfo } from '@/api/login'
import { buildNavMenuViewModels } from '@/utils/permissionMenu'
const { t } = useI18n()
const userStore = useUserStore()
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 props = defineProps({
visible: {
type: Boolean,
default: false
}
})
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 configuredRecords = ref([])
const originConfIds = ref([])
const configuredIds = ref([])
const menuMap = computed(() => {
const map = {}
userMenuList.value.forEach(item => {
map[item.id] = item
})
return map
})
const configuredMenuList = computed(() => {
return configuredIds.value
.map(id => menuMap.value[id])
.filter(Boolean)
})
const unconfiguredMenuList = computed(() => {
return userMenuList.value
.filter(item => !configuredIds.value.includes(item.id))
})
async function loadData() {
try {
const menuRes = await getNavPermissionInfo()
userMenuList.value = buildNavMenuViewModels(menuRes?.data?.menus)
const navRes = await getUserNavMenuList()
configuredRecords.value = Array.isArray(navRes?.data) ? [...navRes.data] : []
configuredRecords.value.sort((left, right) => Number(left?.sort || 0) - Number(right?.sort || 0))
configuredIds.value = configuredRecords.value
.map(item => item.menuId)
.filter(menuId => !!menuMap.value[menuId])
originConfIds.value = [...configuredIds.value]
} catch (error) {
console.error('加载菜单数据失败:', error)
}
}
function handleConfiguredClick(item) {
configuredIds.value = configuredIds.value.filter(id => id !== item.id)
}
function handleUnconfiguredClick(item) {
if (!configuredIds.value.includes(item.id)) {
configuredIds.value.push(item.id)
}
}
function handleReset() {
configuredIds.value = [...originConfIds.value]
}
async function handleConfirm() {
try {
const userId = userStore.userId
const data = configuredIds.value.map((menuId, index) => ({
menuId,
userId,
sort: index,
status: 1
}))
await updateUserNavMenuList(data)
uni.showToast({ title: t('common.updateSuccess'), icon: 'success' })
emit('update')
handleClose()
} catch (error) {
console.error('保存失败:', error)
uni.showToast({ title: t('common.saveFailed'), icon: 'none' })
}
}
function handleClose() {
emit('close')
}
watch(() => props.visible, async (val) => {
if (val) {
await loadData()
}
})
</script>
<style lang="scss" scoped>
.nav-menu-editor-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
align-items: flex-end;
padding-bottom: calc(90rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
transition: opacity 0.3s ease;
&.mask-hidden {
opacity: 0;
}
}
.nav-menu-editor {
width: 100%;
max-height: 75vh;
background: #ffffff;
border-radius: 32rpx 32rpx 0 0;
display: flex;
flex-direction: column;
transition: transform 0.3s ease;
&.panel-hidden {
transform: translateY(100%);
}
}
.editor-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.editor-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
}
.editor-close {
padding: 16rpx;
}
.editor-content {
flex: 1;
overflow-y: auto;
padding: 24rpx;
}
.divider-line {
height: 1rpx;
background: #f0f0f0;
margin: 24rpx 0;
}
.nav-section {
margin-bottom: 8rpx;
}
.nav-section-title {
font-size: 28rpx;
color: #999;
margin-bottom: 20rpx;
display: block;
}
.nav-grid {
display: flex;
flex-wrap: wrap;
min-height: 120rpx;
border-radius: 24rpx;
}
.nav-item {
width: 25%;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 24rpx;
padding: 16rpx;
border-radius: 16rpx;
position: relative;
transition: all 0.15s;
&:active {
opacity: 0.7;
}
}
.nav-icon {
width: 96rpx;
height: 96rpx;
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12rpx;
.nav-icon-text {
font-size: 32rpx;
color: #1a3a5c;
font-weight: 700;
}
}
.nav-text {
font-size: 24rpx;
color: #333;
text-align: center;
&.nav-text-disabled {
color: #999;
}
}
.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 {
font-size: 24rpx;
color: #999;
text-align: center;
display: block;
margin-top: 16rpx;
padding-bottom: 24rpx;
}
.editor-footer {
display: flex;
gap: 24rpx;
padding: 24rpx 32rpx;
border-top: 1rpx solid #f0f0f0;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
}
.btn-reset,
.btn-confirm {
flex: 1;
height: 88rpx;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
}
.btn-reset {
background: #f5f5f5;
color: #666;
}
.btn-confirm {
background: #22486e;
color: #ffffff;
}
</style>

@ -0,0 +1,250 @@
<template>
<view v-if="renderVisible" class="nav-menu-more-mask" :class="{ 'mask-hidden': !animVisible }" @click="handleClose">
<view class="nav-menu-more" :class="{ 'panel-hidden': !animVisible }" @click.stop>
<view class="more-header">
<text class="more-title">{{ t('dashboard.allNavMenu') }}</text>
<view class="more-actions">
<view class="action-btn edit-btn" @click="handleEdit">
<uni-icons type="compose" size="22" color="#22486e"></uni-icons>
</view>
</view>
</view>
<scroll-view scroll-y class="more-content">
<view v-if="displayMenuList.length === 0" class="empty-state">
<text class="empty-text">{{ t('dashboard.clickHint') }}</text>
</view>
<view v-else class="nav-grid">
<view
v-for="item in displayMenuList"
:key="item.id"
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>
</scroll-view>
</view>
</view>
</template>
<script setup>
import { computed, ref, watch, nextTick } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
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 props = defineProps({
visible: {
type: Boolean,
default: false
},
menuList: {
type: Array,
default: () => []
}
})
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 : [])
function handleNavClick(item) {
if (item.route) {
uni.navigateTo({ url: item.route })
} else {
uni.showToast({ title: t('common.moduleBuilding'), icon: 'none' })
}
}
function handleEdit() {
emit('edit')
}
function handleClose() {
emit('close')
}
</script>
<style lang="scss" scoped>
.nav-menu-more-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
align-items: flex-end;
padding-bottom: calc(90rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
transition: opacity 0.3s ease;
&.mask-hidden {
opacity: 0;
}
}
.nav-menu-more {
width: 100%;
max-height: 66.6vh;
background: #ffffff;
border-radius: 32rpx 32rpx 0 0;
display: flex;
flex-direction: column;
transition: transform 0.3s ease;
&.panel-hidden {
transform: translateY(100%);
}
}
.more-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.more-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
}
.more-actions {
display: flex;
align-items: center;
gap: 24rpx;
}
.edit-btn {
justify-content: center;
}
.action-btn {
display: flex;
align-items: center;
gap: 8rpx;
padding: 12rpx 20rpx;
&.edit-btn {
background: rgba(34, 72, 110, 0.1);
border-radius: 20rpx;
font-size: 26rpx;
color: #22486e;
}
&.close-btn {
padding: 12rpx;
}
}
.more-content {
flex: 1;
padding: 24rpx;
}
.empty-state {
min-height: 220rpx;
display: flex;
align-items: center;
justify-content: center;
}
.empty-text {
font-size: 26rpx;
color: #999;
text-align: center;
}
.nav-grid {
display: flex;
flex-wrap: wrap;
}
.nav-item {
width: 25%;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 32rpx;
padding: 16rpx;
&:active {
opacity: 0.7;
}
}
.nav-icon {
width: 100rpx;
height: 100rpx;
border-radius: 28rpx;
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: #333;
text-align: center;
}
</style>

@ -1,46 +1,165 @@
<template>
<view class="nav-section">
<view class="section-title">{{ t('dashboard.functionNav') }}</view>
<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 navList" :key="index" class="nav-item" @click="handleNavClick(item)">
<view class="nav-icon" :style="{ backgroundColor: item.bgColor }">
<text class="nav-icon-text">{{ item.icon }}</text>
<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">{{ t(`dashboard.${item.key}`) }}</text>
<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 { reactive } from 'vue'
import { ref, computed, onMounted, 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 navList = reactive([
{ key: 'mold', icon: '🔧', bgColor: '#1a3a5c', path: '/pages_function/mold' },
{ key: 'equipment', icon: '⚙️', bgColor: '#2d5a87', path: '/pages_function/equipment' },
{ key: 'keypart', icon: '🔩', bgColor: '#3d7ab5', path: '/pages_function/keypart' },
{ key: 'spare', icon: '📦', bgColor: '#4a90c2', path: '/pages_function/spare' },
{ key: 'product', icon: '🧾', bgColor: '#5aa0d2', path: '/pages_function/product' }
])
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 handleNavClick(item) {
const navMap = {
mold: '/pages_function/pages/mold/index',
equipment: '/pages_function/pages/equipment/index',
spare: '/pages_function/pages/spare/index',
keypart: '/pages_function/pages/keypart/index',
product: '/pages_function/pages/product/index'
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 []
}
const url = navMap[item.key]
if (url) {
uni.navigateTo({ url })
if (configuredMenuList.value.length === 0) {
return defaultNavList
}
return configuredMenuList.value
})
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()
})
</script>
<style lang="scss" scoped>
@ -52,11 +171,31 @@ function handleNavClick(item) {
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;
margin-bottom: 24rpx;
}
.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 {
@ -84,7 +223,9 @@ function handleNavClick(item) {
margin-bottom: 16rpx;
.nav-icon-text {
font-size: 44rpx;
font-size: 32rpx;
color: #1a3a5c;
font-weight: 700;
}
}
@ -92,4 +233,4 @@ function handleNavClick(item) {
font-size: 26rpx;
color: #333333;
}
</style>
</style>

@ -1,9 +1,6 @@
// 应用全局配置
const config = {
// baseUrl: 'http://47.106.185.127:48080',127.0.0.1
baseUrl: 'https://besure.ngsk.tech:7001',
// baseUrl: 'http://192.168.5.167:48081',
// baseUrl: '',
baseUrl: import.meta.env.VITE_APP_BASE_URL,
// 应用信息
appInfo: {
// 应用名称

@ -7,7 +7,12 @@ export default {
moduleBuilding: 'This module is under construction',
updateSuccess: 'Updated successfully',
confirmLogout: 'Are you sure you want to log out?',
languageSwitched: 'Language switched'
languageSwitched: 'Language switched',
more: 'More',
reset: 'Reset',
complete: 'Done',
edit: 'Edit',
saveFailed: 'Save failed'
},
tab: {
home: 'Home',
@ -30,6 +35,12 @@ export default {
welcome: 'Welcome to',
subtitle: 'Besure Digital Intelligent Control Platform',
functionNav: 'Function Navigation',
editNavMenu: 'Edit Shortcuts',
configuredNav: 'Added',
unconfiguredNav: 'Add More',
dragHint: 'Drag icons to reorder, icons above will be displayed in control center',
clickHint: 'Tap icons to add or remove from configuration',
allNavMenu: 'All Functions',
productionOverview: 'Production Overview',
qualityOverview: 'Quality Overview',
productionPlan: 'Production Summary',

@ -1,8 +1,6 @@
import { createI18n } from 'vue-i18n'
import zhCN from './zh-CN'
import enUS from './en-US'
import useUserStore from '@/store/modules/user'
import { syncTabBarMenus } from '@/utils/permissionMenu'
const LOCALE_STORAGE_KEY = 'app_locale'
const LOCALE_CHANGE_EVENT = 'app-locale-changed'
@ -85,26 +83,6 @@ const literalMap = {
'点检记录详情': 'moldWorkOrder.detailTitle'
}
export function applyTabBarLanguage() {
try {
const pages = getCurrentPages()
if (!pages || pages.length === 0) return
const currentPage = pages[pages.length - 1]
if (!currentPage) return
const route = currentPage.route || ''
const tabBarPages = ['pages/index', 'pages/report', 'pages/work', 'pages/mine']
if (!tabBarPages.includes(route)) return
syncTabBarMenus(useUserStore().menus, {
homeText: i18n.global.t('tab.home'),
reportFallback: i18n.global.t('tab.report'),
workFallback: i18n.global.t('tab.work'),
mineText: i18n.global.t('tab.mine')
})
} catch (e) {
}
}
export function getCurrentLocale() {
return i18n.global.locale.value
}
@ -113,7 +91,6 @@ export function setLocale(locale) {
const nextLocale = normalizeLocale(locale)
i18n.global.locale.value = nextLocale
uni.setStorageSync(LOCALE_STORAGE_KEY, nextLocale)
applyTabBarLanguage()
uni.$emit(LOCALE_CHANGE_EVENT, nextLocale)
return nextLocale
}

@ -7,7 +7,12 @@ export default {
moduleBuilding: '模块建设中~',
updateSuccess: '修改成功',
confirmLogout: '确定注销并退出系统吗',
languageSwitched: '语言已切换'
languageSwitched: '语言已切换',
more: '更多',
reset: '重置',
complete: '完成',
edit: '编辑',
saveFailed: '保存失败'
},
tab: {
home: '首页',
@ -30,6 +35,12 @@ export default {
welcome: '欢迎您使用',
subtitle: '必硕数字化智能中控平台',
functionNav: '功能导航',
editNavMenu: '编辑快捷开关',
configuredNav: '已添加',
unconfiguredNav: '添加更多',
dragHint: '拖动图标进行排序,上方图标将显示在控制中心',
clickHint: '点击图标添加到已配置或取消配置',
allNavMenu: '全部功能',
productionOverview: '生产整体概况',
qualityOverview: '质量概况',
productionPlan: '生产概括',

@ -11,9 +11,6 @@ import { translateLiteral } from '@/locales'
import { useDict } from '@/utils/dict'
import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi'
import { applyNavigationBarTheme } from '@/utils/navigationBar'
export function createApp() {
const app = createSSRApp(App)
@ -33,12 +30,6 @@ export function createApp() {
app.config.globalProperties.selectDictLabels = selectDictLabels
app.config.globalProperties.$tl = translateLiteral
app.mixin({
onShow() {
applyNavigationBarTheme()
}
})
return {
app
}

@ -1,5 +1,6 @@
<template>
<view class="container">
<NavBar title="新增投料记录" />
<view class="example">
<uni-forms ref="customForm" :rules="customRules" labelWidth="60px" :modelValue="formData">
@ -52,6 +53,7 @@
</template>
<script>
import NavBar from '@/components/common/NavBar.vue'
import {create} from "@/api/mes/record"
import {getUnitList,getItemList} from "@/api/mes/product"
import { pipelineTypes,feedingTypes, findTextByValue} from "@/api/system/dict/data";
@ -59,7 +61,10 @@ import {showConfirm} from "@/utils/common";
import tab from "@/plugins/tab";
import modal from "@/plugins/modal";
export default {
components: {},
components: {
NavBar
},
name: "feedingRecordForm",
data() {
return {
//

@ -1,5 +1,6 @@
<template>
<view class="container">
<NavBar title="计划进度" />
<uni-card :is-shadow="false" is-full>
<uni-row>
<uni-col :span="10">
@ -45,6 +46,7 @@
</template>
<script>
import NavBar from '@/components/common/NavBar.vue'
import {processTypes,findTextByValue} from "@/api/system/dict/data";
import {getPlanProgress} from "@/api/mes/plan"
@ -52,7 +54,7 @@ import {getPlanProgress} from "@/api/mes/plan"
import tab from "@/plugins/tab";
import modal from "@/plugins/modal";
export default {
components: {},
components: { NavBar },
data() {
return {
planDo: undefined,

@ -1,5 +1,6 @@
<template>
<view class="container">
<NavBar title="代报工" />
<view class="example">
<!-- 自定义表单校验 -->
<uni-forms ref="customForm" :rules="customRules" labelWidth="60px" :modelValue="customFormData">
@ -91,6 +92,7 @@
</template>
<script>
import NavBar from '@/components/common/NavBar.vue'
import {processTypes, groupTypes} from "@/api/system/dict/data";
import {
@ -106,7 +108,9 @@ import tab from "@/plugins/tab";
import modal from "@/plugins/modal";
import {getById, getDetailByReportId, update} from "@/api/mes/report";
export default {
components: {},
components: {
NavBar
},
data() {
return {
//

@ -1,5 +1,6 @@
<template>
<view class="container">
<NavBar title="生产报工" />
<uni-card :is-shadow="false" is-full>
<text class="uni-h6">如个人无法填报请寻找主管代为报工</text>
</uni-card>
@ -91,6 +92,7 @@
</template>
<script>
import NavBar from '@/components/common/NavBar.vue'
import useUserStore from "@/store/modules/user";
import {processTypes, groupTypes} from "@/api/system/dict/data";
@ -100,7 +102,7 @@ import {showConfirm} from "@/utils/common";
import tab from "@/plugins/tab";
import modal from "@/plugins/modal";
export default {
components: {},
components: { NavBar },
data() {
let userStore = {
name: '',

@ -11,65 +11,60 @@
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
},
"globalStyle": {
"navigationBarTitleText": "BESURE"
},
"pages": [
{
"path": "pages/index",
"style": {
"navigationBarTitleText": "首页",
"enablePullDownRefresh": false,
"navigationBarBackgroundColor": "#22486e",
"navigationBarTextStyle": "white"
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/login",
"style": {
"navigationBarTitleText": "登录",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
"navigationStyle": "custom"
}
},
{
"path": "pages/work",
"style": {
"navigationBarTitleText": "管理",
"enablePullDownRefresh": false,
"navigationBarBackgroundColor": "#22486e",
"navigationBarTextStyle": "white"
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/mine",
"style": {
"navigationBarTitleText": "个人中心",
"navigationBarBackgroundColor": "#22486e",
"navigationBarTextStyle": "white"
"navigationStyle": "custom"
}
},
{
"path": "pages/common/webview/index",
"style": {
"navigationBarTitleText": "浏览网页",
"navigationBarBackgroundColor": "#22486e",
"navigationBarTextStyle": "white"
"navigationStyle": "custom"
}
},
{
"path": "pages/common/textview/index",
"style": {
"navigationBarTitleText": "浏览文本",
"navigationBarBackgroundColor": "#22486e",
"navigationBarTextStyle": "white"
"navigationStyle": "custom"
}
},
{
"path": "pages/report",
"style": {
"navigationBarTitleText": "报表",
"enablePullDownRefresh": false,
"navigationBarBackgroundColor": "#22486e",
"navigationBarTextStyle": "white"
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
}
],
@ -80,20 +75,22 @@
{
"path": "reportForm",
"style": {
"navigationBarTitleText": "生产报工"
"navigationBarTitleText": "生产报工",
"navigationStyle": "custom"
}
},
{
"path": "replaceForm",
"style": {
"navigationBarTitleText": "代报工"
"navigationBarTitleText": "代报工",
"navigationStyle": "custom"
}
}
,
},
{
"path": "planProgress",
"style": {
"navigationBarTitleText": "计划进度"
"navigationBarTitleText": "计划进度",
"navigationStyle": "custom"
}
}
]
@ -104,7 +101,8 @@
{
"path": "feedingRecordForm",
"style": {
"navigationBarTitleText": "新增投料记录"
"navigationBarTitleText": "新增投料记录",
"navigationStyle": "custom"
}
}
]
@ -115,43 +113,50 @@
{
"path": "avatar/index",
"style": {
"navigationBarTitleText": "修改头像"
"navigationBarTitleText": "修改头像",
"navigationStyle": "custom"
}
},
{
"path": "info/index",
"style": {
"navigationBarTitleText": "个人信息"
"navigationBarTitleText": "个人信息",
"navigationStyle": "custom"
}
},
{
"path": "info/edit",
"style": {
"navigationBarTitleText": "编辑资料"
"navigationBarTitleText": "编辑资料",
"navigationStyle": "custom"
}
},
{
"path": "pwd/index",
"style": {
"navigationBarTitleText": "修改密码"
"navigationBarTitleText": "修改密码",
"navigationStyle": "custom"
}
},
{
"path": "setting/index",
"style": {
"navigationBarTitleText": "应用设置"
"navigationBarTitleText": "应用设置",
"navigationStyle": "custom"
}
},
{
"path": "help/index",
"style": {
"navigationBarTitleText": "常见问题"
"navigationBarTitleText": "常见问题",
"navigationStyle": "custom"
}
},
{
"path": "about/index",
"style": {
"navigationBarTitleText": "关于我们"
"navigationBarTitleText": "关于我们",
"navigationStyle": "custom"
}
}
]
@ -606,53 +611,19 @@
{
"path": "planList/index",
"style": {
"navigationBarTitleText": "生产计划"
"navigationBarTitleText": "生产计划",
"navigationStyle": "custom"
}
},
{
"path": "taskList/index",
"style": {
"navigationBarTitleText": "任务计划"
"navigationBarTitleText": "任务计划",
"navigationStyle": "custom"
}
}
]
}
],
"tabBar": {
"color": "#666666",
"selectedColor": "#1a3a5c",
"borderStyle": "white",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index",
"iconPath": "static/images/tabbar/home.png",
"selectedIconPath": "static/images/tabbar/home_.png",
"text": "首页"
},
{
"pagePath": "pages/report",
"iconPath": "static/images/tabbar/report.png",
"selectedIconPath": "static/images/tabbar/report_.png",
"text": "报表"
},
{
"pagePath": "pages/work",
"iconPath": "static/images/tabbar/work.png",
"selectedIconPath": "static/images/tabbar/work_.png",
"text": "管理"
},
{
"pagePath": "pages/mine",
"iconPath": "static/images/tabbar/mine.png",
"selectedIconPath": "static/images/tabbar/mine_.png",
"text": "我的"
}
]
},
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "BESURE",
"navigationBarBackgroundColor": "#22486e"
}
}
"preloadRule": {}
}

@ -1,5 +1,6 @@
<template>
<view>
<NavBar :title="title" />
<uni-card class="view-title" :title="title">
<text class="uni-body view-content">{{ content }}</text>
</uni-card>
@ -7,7 +8,12 @@
</template>
<script>
import NavBar from '@/components/common/NavBar.vue'
export default {
components: {
NavBar
},
data() {
return {
title: '',
@ -17,9 +23,6 @@
onLoad(options) {
this.title = options.title
this.content = options.content
uni.setNavigationBarTitle({
title: options.title
})
}
}
</script>

@ -1,11 +1,17 @@
<template>
<view v-if="params.url">
<web-view :webview-styles="webviewStyles" :src="`${params.url}`"></web-view>
<view>
<NavBar v-if="params.title" :title="params.title" />
<web-view v-if="params.url" :webview-styles="webviewStyles" :src="`${params.url}`"></web-view>
</view>
</template>
<script>
import NavBar from '@/components/common/NavBar.vue'
export default {
components: {
NavBar
},
data() {
return {
params: {},
@ -24,11 +30,6 @@
},
onLoad(event) {
this.params = event
if (event.title) {
uni.setNavigationBarTitle({
title: event.title
})
}
}
}
</script>

@ -1,11 +1,10 @@
<template>
<view class="page-container">
<NavBar :title="pageTitle" />
<scroll-view scroll-y class="main-scroll" :scroll-top="scrollTop" scroll-with-animation @scroll="onScroll">
<BannerSection />
<view class="content-section">
<!-- 功能导航 -->
<NavSection />
<!-- 生产/质量概览统计 -->
<StatsSection v-if="currentMode === 'production'" @mode-change="onModeChange" />
<QualitySection v-else @mode-change="onModeChange" />
</view>
@ -14,18 +13,25 @@
<view v-if="showGoTop" class="go-top-btn" @click="goTop">
<text class="go-top-icon"></text>
</view>
<TabBar />
</view>
</template>
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { onMounted, onUnmounted, ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { onLocaleChange, offLocaleChange, setNavigationTitle } from '@/locales'
import NavBar from '@/components/common/NavBar.vue'
import TabBar from '@/components/common/TabBar.vue'
import BannerSection from '@/components/dashboard/BannerSection.vue'
import NavSection from '@/components/dashboard/NavSection.vue'
import StatsSection from '@/components/dashboard/StatsSection.vue'
import QualitySection from '@/components/dashboard/QualitySection.vue'
const { t } = useI18n()
const pageTitle = computed(() => t('nav.home'))
const scrollTop = ref(0)
const currentMode = ref('production')
@ -47,15 +53,6 @@ function goTop() {
scrollTop.value = 0
}, 0)
}
const updatePageTitle = () => setNavigationTitle('nav.home')
onShow(() => {
updatePageTitle()
})
onLocaleChange(updatePageTitle)
onUnmounted(() => {
offLocaleChange(updatePageTitle)
})
</script>
<style lang="scss" scoped>
@ -64,6 +61,7 @@ onUnmounted(() => {
flex-direction: column;
height: 100vh;
background-color: #f0f2f5;
padding-bottom: 100rpx;
}
.main-scroll {
@ -75,7 +73,7 @@ onUnmounted(() => {
padding: 0 24rpx 24rpx;
margin-top: -40rpx;
position: relative;
z-index: 5;
z-index: 1;
}
.go-top-btn {
@ -90,7 +88,7 @@ onUnmounted(() => {
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 24rpx rgba(26, 58, 92, 0.3);
z-index: 10;
z-index: 50;
&:active {
transform: scale(0.95);

@ -1,5 +1,6 @@
<template>
<view class="scroll-container">
<NavBar title="登录" />
<view class="login-container">
<!-- Logo区域 -->
@ -51,6 +52,7 @@ import { getCodeImg } from '@/api/login'
import { ref } from "vue";
import config from '@/config.js'
import useUserStore from '@/store/modules/user'
import NavBar from '@/components/common/NavBar.vue'
const userStore = useUserStore()
const codeUrl = ref("");
@ -104,7 +106,7 @@ async function pwdLogin() {
function loginSuccess(result) {
userStore.getInfo().then(res => {
uni.switchTab({
uni.reLaunch({
url: '/pages/index'
});
})

@ -1,5 +1,6 @@
<template>
<view class="mine-container" :style="{ height: `${windowHeight}px` }">
<NavBar :title="pageTitle" />
<view class="header-section">
<view class="flex padding justify-between">
<view class="flex align-center">
@ -79,19 +80,23 @@
</uni-popup-dialog>
</uni-popup>
</view>
<TabBar />
</view>
</template>
<script setup>
import { onUnmounted, ref } from "vue";
import { onShow } from '@dcloudio/uni-app'
import { computed, onUnmounted, ref } from "vue";
import { useI18n } from 'vue-i18n'
import useUserStore from '@/store/modules/user'
import { onLocaleChange, offLocaleChange, setNavigationTitle } from '@/locales'
import { onLocaleChange, offLocaleChange } from '@/locales'
import NavBar from '@/components/common/NavBar.vue'
import TabBar from '@/components/common/TabBar.vue'
const userStore = useUserStore()
const name = userStore.name;
const { t } = useI18n()
const pageTitle = computed(() => t('nav.mine'))
const avatar = ref(userStore.avatar);
const windowHeight = ref(uni.getSystemInfoSync().windowHeight - 50);
const popup = ref(null);
@ -100,12 +105,6 @@ uni.$on('refresh', () => {
avatar.value = userStore.avatar;
})
const updatePageTitle = () => setNavigationTitle('nav.mine')
onShow(() => {
updatePageTitle()
})
onLocaleChange(updatePageTitle)
function handleToInfo() {
uni.navigateTo({
url: '/pages_mine/pages/info/index'
@ -167,12 +166,71 @@ function handleBuilding() {
duration: 1000
});
}
</script>
onUnmounted(() => {
offLocaleChange(updatePageTitle)
})
<style lang="scss">
page {
background-color: #f5f6f7;
}
</script>
.mine-container {
width: 100%;
height: 100%;
.header-section {
padding: 15px 15px 45px 15px;
background: linear-gradient(135deg, #1a3a5c 0%, #2d5a87 100%);
color: white;
.login-tip {
font-size: 18px;
margin-left: 10px;
}
.cu-avatar {
border: 2px solid #eaeaea;
.icon {
font-size: 40px;
}
}
.user-info {
margin-left: 15px;
.u_title {
font-size: 18px;
line-height: 30px;
}
}
}
.content-section {
position: relative;
top: -50px;
.mine-actions {
margin: 15px 15px;
padding: 20px 0px;
border-radius: 8px;
background-color: white;
.action-item {
.icon {
font-size: 28px;
}
.text {
display: block;
font-size: 13px;
margin: 8px 0px;
}
}
}
}
}
</style>
<style lang="scss">
page {

@ -1,12 +1,32 @@
<template>
<!-- 原固定报表菜单已移除改为根据权限接口返回的 menus 动态渲染 -->
<PermissionMenuPage
page-path="pages/report"
title="报表中心"
subtitle="数据驱动决策 · 智能分析"
/>
<view class="page-container">
<NavBar :title="pageTitle" />
<PermissionMenuPage
page-path="pages/report"
title="报表中心"
subtitle="数据驱动决策 · 智能分析"
/>
<TabBar />
</view>
</template>
<script setup>
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import TabBar from '@/components/common/TabBar.vue'
import PermissionMenuPage from '@/components/common/PermissionMenuPage.vue'
const { t } = useI18n()
const pageTitle = computed(() => t('tab.report'))
</script>
<style lang="scss" scoped>
.page-container {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f6f7;
padding-bottom: 100rpx;
}
</style>

@ -1,14 +1,34 @@
<template>
<!-- 原固定管理菜单已移除改为根据权限接口返回的 menus 动态渲染 -->
<PermissionMenuPage
page-path="pages/work"
title="管理中心"
subtitle="系统配置与管理"
:searchable="true"
:show-go-top="true"
/>
<view class="page-container">
<NavBar :title="pageTitle" />
<PermissionMenuPage
page-path="pages/work"
title="管理中心"
subtitle="系统配置与管理"
:searchable="true"
:show-go-top="true"
/>
<TabBar />
</view>
</template>
<script setup>
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import TabBar from '@/components/common/TabBar.vue'
import PermissionMenuPage from '@/components/common/PermissionMenuPage.vue'
const { t } = useI18n()
const pageTitle = computed(() => t('tab.work'))
</script>
<style lang="scss" scoped>
.page-container {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f6f7;
padding-bottom: 100rpx;
}
</style>

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('criticalComponent.detailTitle')" />
<NavBar :title="t('criticalComponent.detailTitle')" />
</view>
<scroll-view scroll-y class="detail-scroll">
@ -53,7 +53,7 @@
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getCriticalComponent } from '@/api/mes/criticalComponent'
const { t } = useI18n()

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('criticalComponent.moduleName')" :subTitle="t('criticalComponent.subTitle')" :showSubTitle="true" />
<NavBar :title="t('criticalComponent.moduleName')" :subTitle="t('criticalComponent.subTitle')" />
<!-- 搜索区域 -->
<view class="search-card">
@ -132,7 +132,7 @@
import { ref, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getCriticalComponentPage, getCriticalComponent, createCriticalComponent, updateCriticalComponent, deleteCriticalComponent } from '@/api/mes/criticalComponent'
const { t } = useI18n()

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="设备详情" />
<NavBar title="设备详情" />
<view class="content-section">
<view class="info-card">
@ -203,7 +203,7 @@
<script setup>
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import {
getEquipmentDetail,
getEquipmentInspectionByDeviceId,

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="设备查询" subTitle="请选择查询方式" :showSubTitle="true" />
<NavBar title="设备查询" subTitle="请选择查询方式" />
<view class="content-section">
<view class="scan-section">
@ -53,7 +53,7 @@
<script setup>
import { ref } from 'vue';
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
const equipmentCode = ref('');
const isScanning = ref(false);

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('equipmentCategory.detailTitle')" />
<NavBar :title="t('equipmentCategory.detailTitle')" />
</view>
<scroll-view scroll-y class="detail-scroll">
@ -45,7 +45,7 @@
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getDeviceType, getDeviceTypeTree } from '@/api/mes/deviceType'
const { t } = useI18n()

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('equipmentCategory.moduleName')" :subTitle="t('equipmentCategory.subTitle')" :showSubTitle="true" />
<NavBar :title="t('equipmentCategory.moduleName')" :subTitle="t('equipmentCategory.subTitle')" />
<!-- 搜索区域 -->
<view class="search-card">
@ -118,7 +118,7 @@
import { ref, reactive, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getDeviceTypeTree, getDeviceType, createDeviceType, updateDeviceType, deleteDeviceType } from '@/api/mes/deviceType'
const { t } = useI18n()

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('equipmentLedger.detailTitle')" />
<NavBar :title="t('equipmentLedger.detailTitle')" />
</view>
<scroll-view scroll-y class="detail-scroll">
@ -148,7 +148,7 @@
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getDeviceLedger } from '@/api/mes/deviceLedger'
import { getDeviceTypeTree } from '@/api/mes/deviceType'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('equipmentLedger.moduleName')" :subTitle="t('equipmentLedger.subTitle')" :showSubTitle="true" />
<NavBar :title="t('equipmentLedger.moduleName')" :subTitle="t('equipmentLedger.subTitle')" />
<!-- 搜索区域 -->
<view class="search-card">
@ -186,7 +186,7 @@
import { ref, reactive, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getDeviceLedgerPage, getDeviceLedger, createDeviceLedger, updateDeviceLedger, deleteDeviceLedger } from '@/api/mes/deviceLedger'
import { getDeviceTypeTree } from '@/api/mes/deviceType'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="关键件详情" />
<NavBar title="关键件详情" />
<view class="content-section">
<view class="info-card">
@ -36,7 +36,7 @@
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import request from '@/utils/request'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
const keypartId = ref(undefined)
const keypartCode = ref(undefined)

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="关键件查询" subTitle="请选择查询方式" :showSubTitle="true" />
<NavBar title="关键件查询" subTitle="请选择查询方式" />
<view class="content-section">
<view class="scan-section">
@ -53,7 +53,7 @@
<script setup>
import { ref } from 'vue';
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
const keypartCode = ref('');
const isScanning = ref(false);

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('materialCategory.detailTitle')" />
<NavBar :title="t('materialCategory.detailTitle')" />
</view>
<scroll-view scroll-y class="detail-scroll">
@ -45,7 +45,7 @@
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getProductCategory, getProductCategoryList } from '@/api/erp/productCategory'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('materialCategory.moduleName')" :subTitle="t('materialCategory.subTitle')" :showSubTitle="true" />
<NavBar :title="t('materialCategory.moduleName')" :subTitle="t('materialCategory.subTitle')" />
<!-- 搜索区域 -->
<view class="search-card">
@ -116,7 +116,7 @@
import { ref, reactive, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getProductCategoryList, getProductCategorySimpleList, createProductCategory, updateProductCategory, deleteProductCategory } from '@/api/erp/productCategory'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('materialInfo.detailTitle')" />
<NavBar :title="t('materialInfo.detailTitle')" />
</view>
<scroll-view scroll-y class="detail-scroll">
@ -57,7 +57,7 @@
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getProduct } from '@/api/erp/productInfo'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('materialInfo.moduleName')" :subTitle="t('materialInfo.subTitle')" :showSubTitle="true" />
<NavBar :title="t('materialInfo.moduleName')" :subTitle="t('materialInfo.subTitle')" />
<!-- 搜索区域 -->
<view class="search-card">
@ -149,7 +149,7 @@
import { ref, reactive, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getProductPage, getProduct, createProduct, updateProduct, deleteProduct, getProductUnitSimpleList } from '@/api/erp/productInfo'
import { getProductCategorySimpleList } from '@/api/erp/productCategory'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="模具详情" />
<NavBar title="模具详情" />
<view class="content-section">
<view class="info-card">
@ -196,7 +196,7 @@
<script setup>
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import {
getMoldDetail,
getMoldInspectionByMoldId,

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="模具查询" subTitle="请选择查询方式" :showSubTitle="true" />
<NavBar title="模具查询" subTitle="请选择查询方式" />
<view class="content-section">
<view class="scan-section">
@ -48,7 +48,7 @@
<script setup>
import { ref } from 'vue';
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
const moldCode = ref('');
const isScanning = ref(false);

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('moldInspectionItems.detailTitle')" />
<NavBar :title="t('moldInspectionItems.detailTitle')" />
</view>
<view class="content-section">
<view class="info-card">
@ -49,7 +49,7 @@
import { reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { DICT_TYPE, useDict } from '@/utils/dict'
import { getMoldInspectionItemDetail } from '@/api/mes/moldInspectionItems'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('moldInspectionItems.moduleName')" :subTitle="t('moldInspectionItems.subTitle')" :showSubTitle="true" />
<NavBar :title="t('moldInspectionItems.moduleName')" :subTitle="t('moldInspectionItems.subTitle')" />
<!-- 查询区域 -->
<view class="search-card">
@ -125,7 +125,7 @@
import { computed, reactive, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { DICT_TYPE, useDict } from '@/utils/dict'
import { getMoldInspectionItemPage, getMoldInspectionItemDetail, createMoldInspectionItem, updateMoldInspectionItem, deleteMoldInspectionItem } from '@/api/mes/moldInspectionItems'

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('moldInspectionPlan.detailTitle')" />
<NavBar :title="t('moldInspectionPlan.detailTitle')" />
</view>
<scroll-view scroll-y class="detail-scroll">
@ -75,7 +75,7 @@
import { computed, reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'
import { getMoldInspectionPlanDetail } from '@/api/mes/moldInspectionPlan'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('moldInspectionPlan.moduleName')" :subTitle="t('moldInspectionPlan.subTitle')" :showSubTitle="true" />
<NavBar :title="t('moldInspectionPlan.moduleName')" :subTitle="t('moldInspectionPlan.subTitle')" />
<view class="search-card">
<view class="search-row">
@ -140,7 +140,7 @@
import { computed, reactive, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getMoldInspectionPlanPage, createMoldInspectionPlan, updateMoldInspectionPlan, deleteMoldInspectionPlan, getMoldSubjectAllList } from '@/api/mes/moldInspectionPlan'
const { t } = useI18n()

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader title="模具台账详情" />
<NavBar title="模具台账详情" />
</view>
<view class="content-section">
@ -194,7 +194,7 @@
<script setup>
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getMoldDetail, getMoldInspectionByMoldId, getMoldMaintenanceByMoldId, getMoldRepairListByMoldId } from '@/api/mes/mold'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="模具台账" subTitle="按型号、状态、编码快速筛选" :showSubTitle="true" />
<NavBar title="模具台账" subTitle="按型号、状态、编码快速筛选" />
<view class="filter-card">
<view class="filter-row">
@ -275,7 +275,7 @@
<script setup>
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import CopyButton from '@/components/common/CopyButton.vue'
import { createMold, deleteMold, getMoldBrandTree, getMoldDetail, getMoldPage, updateMold, uploadMoldImage } from '@/api/mes/mold'
import { getProductUnitSimpleList, getUnitList } from '@/api/mes/product'

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('moldTaskConfig.detailTitle')" />
<NavBar :title="t('moldTaskConfig.detailTitle')" />
</view>
<scroll-view scroll-y class="detail-scroll">
@ -65,7 +65,7 @@
import { computed, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
const { t } = useI18n()

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('moldTaskConfig.moduleName')" :subTitle="t('moldTaskConfig.subTitle')" :showSubTitle="true" />
<NavBar :title="t('moldTaskConfig.moduleName')" :subTitle="t('moldTaskConfig.subTitle')" />
<view class="search-card">
<view class="search-row">
@ -246,7 +246,7 @@
import { ref, reactive, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getTaskManagementPage, createTaskManagement, updateTaskManagement, deleteTaskManagement, createTaskManagementTicket } from '@/api/mes/moldTaskConfiguration'
import { getMoldList } from '@/api/mes/mold'
import { getMoldInspectionPlanPage } from '@/api/mes/moldInspectionPlan'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="模具类型详情" />
<NavBar title="模具类型详情" />
<view class="content-section">
<view class="info-card">
@ -63,7 +63,7 @@
<script setup>
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getMoldBrandDetail } from '@/api/mes/mold'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="模具类型" subTitle="按编码或名称快速查询" :showSubTitle="true" />
<NavBar title="模具类型" subTitle="按编码或名称快速查询" />
<view class="search-section">
<view class="search-wrapper">
@ -146,7 +146,7 @@
<script setup>
import { ref, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import CopyButton from '@/components/common/CopyButton.vue'
import { createMoldBrand, deleteMoldBrand, getMoldBrandDetail, getMoldBrandPage, updateMoldBrand } from '@/api/mes/mold'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('moldWorkOrder.detailTitle')" />
<NavBar :title="t('moldWorkOrder.detailTitle')" />
</view>
<scroll-view scroll-y class="detail-scroll">
@ -116,7 +116,7 @@
import { computed, reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getTicketResultsPage } from '@/api/mes/moldWorkOrderInquiry'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('moldWorkOrder.moduleName')" :subTitle="t('moldWorkOrder.subTitle')" :showSubTitle="true" />
<NavBar :title="t('moldWorkOrder.moduleName')" :subTitle="t('moldWorkOrder.subTitle')" />
<!-- 搜索区域 -->
<view class="search-card">
@ -109,7 +109,7 @@
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getTicketManagementPage, batchUpdateTicketStatus } from '@/api/mes/moldWorkOrderInquiry'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('moldGet.detailTitle')" />
<NavBar :title="t('moldGet.detailTitle')" />
</view>
<view class="content-section">
<view class="info-card">
@ -67,7 +67,7 @@
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getMoldGetDetail } from '@/api/mes/moldget'
import { DICT_TYPE, useDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('moldGet.moduleName')" :subTitle="t('moldGet.subTitle')" :showSubTitle="true" />
<NavBar :title="t('moldGet.moduleName')" :subTitle="t('moldGet.subTitle')" />
<view class="search-card">
<view class="search-row">
@ -181,7 +181,7 @@
import { computed, reactive, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getMoldList } from '@/api/mes/mold'
import { getMoldGetPage, getMoldGetDetail, createMoldGet, updateMoldGet, updateMoldGetStatus, deleteMoldGet, getWarehouseSimpleList, getSimpleUserList } from '@/api/mes/moldget'
import { DICT_TYPE, useDict } from '@/utils/dict'

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('moldOperate.detailTitle')" />
<NavBar :title="t('moldOperate.detailTitle')" />
</view>
<view class="content-section">
@ -54,7 +54,7 @@
import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getMoldOperateDetail } from '@/api/mes/moldoperate'
const { t } = useI18n()

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('moldOperate.moduleName')" :subTitle="t('moldOperate.subTitle')" :showSubTitle="true" />
<NavBar :title="t('moldOperate.moduleName')" :subTitle="t('moldOperate.subTitle')" />
<!-- 操作类型切换 -->
<view class="operate-tabs">
@ -156,7 +156,7 @@
import { computed, reactive, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getMoldList, getInTransitMoldAllList } from '@/api/mes/mold'
import { getMoldOperatePage, getMoldOperateDetail, createMoldOperate, updateMoldOperate, deleteMoldOperate, getLowerMoldList, getDeviceLedgerList } from '@/api/mes/moldoperate'

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('moldReturn.detailTitle')" />
<NavBar :title="t('moldReturn.detailTitle')" />
</view>
<view class="content-section">
@ -70,7 +70,7 @@
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getMoldReturnDetail } from '@/api/mes/moldreturn'
import { DICT_TYPE, useDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('moldReturn.moduleName')" :subTitle="t('moldReturn.subTitle')" :showSubTitle="true" />
<NavBar :title="t('moldReturn.moduleName')" :subTitle="t('moldReturn.subTitle')" />
<!-- 列表查询区 -->
<view class="search-card">
@ -195,7 +195,7 @@
import { computed, reactive, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getMoldList } from '@/api/mes/mold'
import { getMoldReturnPage, getMoldReturnDetail, createMoldReturn, updateMoldReturn, updateMoldReturnStatus, deleteMoldReturn } from '@/api/mes/moldreturn'
import { getWarehouseSimpleList } from '@/api/mes/moldget'

@ -1,5 +1,6 @@
<template>
<view class="page-container">
<NavBar :title="pageTitle" />
<view class="plan-list">
<view v-for="(item, index) in planList" :key="index" class="plan-card" @click="handleItemClick(item)">
<view class="plan-header">
@ -59,15 +60,15 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, computed, reactive, onMounted } from 'vue'
import { onReachBottom, onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import request from '@/utils/request'
import { setNavigationTitle } from '@/locales'
import NavBar from '@/components/common/NavBar.vue'
const { t } = useI18n()
const planList = reactive([])
const planList = ref([])
const pageTitle = computed(() => t('dashboard.productionPlan'))
const pageNo = ref(1)
const pageSize = ref(10)
const total = ref(0)
@ -145,7 +146,6 @@ onReachBottom(() => {
})
onShow(() => {
setNavigationTitle('dashboard.productionPlan')
})
</script>

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="产品物料详情" />
<NavBar title="产品物料详情" />
<view class="content-section">
<view class="info-card">
@ -52,7 +52,7 @@
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import request from '@/utils/request'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
const productId = ref(undefined)
const productCode = ref(undefined)

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="产品物料查询" subTitle="请选择查询方式" :showSubTitle="true" />
<NavBar title="产品物料查询" subTitle="请选择查询方式" />
<view class="content-section">
<view class="scan-section">
@ -53,7 +53,7 @@
<script setup>
import { ref } from 'vue'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
const productId = ref('')
const isScanning = ref(false)

@ -1,7 +1,7 @@
<template>
<view class="page-container">
<view class="fixed-header">
<AppTitleHeader :title="t('productBom.detailTitle')" />
<NavBar :title="t('productBom.detailTitle')" />
</view>
<scroll-view scroll-y class="detail-scroll">
@ -85,7 +85,7 @@
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getBom, getBomDetailListByBomId } from '@/api/mes/productBom'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader :title="t('productBom.moduleName')" :subTitle="t('productBom.subTitle')" :showSubTitle="true" />
<NavBar :title="t('productBom.moduleName')" :subTitle="t('productBom.subTitle')" />
<!-- 搜索区域 -->
<view class="search-card">
@ -134,7 +134,7 @@
import { ref, reactive, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
import { getBomPage, getBom, createBom, updateBom, deleteBom } from '@/api/mes/productBom'
import { getMesProductSimpleList, getItemSimpleList, getProductUnitSimpleList } from '@/api/erp/productInfo'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="备件详情" />
<NavBar title="备件详情" />
<view class="content-section">
<view class="info-card">
@ -44,7 +44,7 @@
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import request from '@/utils/request'
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
const spareId = ref(undefined)
const spareCode = ref(undefined)

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<AppTitleHeader title="备件查询" subTitle="请选择查询方式" :showSubTitle="true" />
<NavBar title="备件查询" subTitle="请选择查询方式" />
<view class="content-section">
<view class="scan-section">
@ -53,7 +53,7 @@
<script setup>
import { ref } from 'vue';
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
import NavBar from '@/components/common/NavBar.vue'
const spareCode = ref('');
const isScanning = ref(false);

@ -1,5 +1,6 @@
<template>
<view class="page-container">
<NavBar :title="pageTitle" />
<view class="filter-bar">
<view class="filter-toggle" @click="showFilter = !showFilter">
<text class="filter-toggle-text">{{ t('taskList.filter') }}</text>
@ -106,11 +107,12 @@ import { ref, reactive, computed, onMounted } from 'vue'
import { onReachBottom, onShow, onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import request from '@/utils/request'
import { setNavigationTitle } from '@/locales'
import NavBar from '@/components/common/NavBar.vue'
import { DICT_TYPE, getDictLabel, useDict } from '@/utils/dict'
const { t } = useI18n()
const { mes_task_status } = useDict(DICT_TYPE.MES_TASK_STATUS)
const pageTitle = computed(() => t('dashboard.production'))
const statusOptions = computed(() => {
return [{ label: t('functionCommon.all'), value: '' }, ...(mes_task_status.value || [])]
@ -261,7 +263,6 @@ onReachBottom(() => {
})
onShow(() => {
setNavigationTitle('dashboard.productionPlan')
})
</script>

@ -1,5 +1,6 @@
<template>
<view class="about-container">
<NavBar :title="pageTitle" />
<view class="header-section text-center">
<image style="width: 150rpx;height: 150rpx;" src="/static/logo.png" mode="widthFix">
</image>
@ -44,24 +45,15 @@
</template>
<script setup>
import { onUnmounted } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import config from '@/config.js'
import { onLocaleChange, offLocaleChange, setNavigationTitle } from '@/locales'
import NavBar from '@/components/common/NavBar.vue'
const { t } = useI18n()
const pageTitle = computed(() => t('nav.about'))
const url = config.appInfo.site_url;
const version = config.appInfo.version;
const updatePageTitle = () => setNavigationTitle('nav.about')
onShow(() => {
updatePageTitle()
})
onLocaleChange(updatePageTitle)
onUnmounted(() => {
offLocaleChange(updatePageTitle)
})
</script>
<style lang="scss">

@ -1,5 +1,6 @@
<template>
<view class="container">
<NavBar :title="$t('nav.avatar')" />
<view class="page-body uni-content-info">
<view class='cropper-content'>
<view v-if="isShowImg" class="uni-corpper"
@ -50,6 +51,7 @@
</template>
<script>
import NavBar from '@/components/common/NavBar.vue'
import config from '@/config'
import { uploadAvatar } from "@/api/system/user"
import useUserStore from '@/store/modules/user'
@ -76,6 +78,9 @@ let PAGE_X, // 手按下的x位置
DRAW_IMAGE_W = sysInfo.screenWidth //
export default {
components: {
NavBar
},
/**
* 页面的初始数据
*/
@ -114,20 +119,12 @@ export default {
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
uni.setNavigationBarTitle({
title: this.$t('nav.avatar')
})
uni.$on('app-locale-changed', this.handleLocaleChange)
this.loadImage()
},
onUnload() {
uni.$off('app-locale-changed', this.handleLocaleChange)
},
methods: {
handleLocaleChange() {
uni.setNavigationBarTitle({
title: this.$t('nav.avatar')
})
},
setData: function (obj) {
let that = this

@ -1,5 +1,6 @@
<template>
<view class="help-container">
<NavBar :title="pageTitle" />
<view v-for="(item, findex) in list" :key="findex" :title="item.title" class="list-title">
<view class="text-title">
<view :class="item.icon"></view>{{ item.title }}
@ -16,12 +17,12 @@
</template>
<script setup>
import { computed, onUnmounted } from "vue";
import { onShow } from '@dcloudio/uni-app'
import { computed } from "vue";
import { useI18n } from 'vue-i18n'
import { onLocaleChange, offLocaleChange, setNavigationTitle } from '@/locales'
import NavBar from '@/components/common/NavBar.vue'
const { t } = useI18n()
const pageTitle = computed(() => t('nav.help'))
const list = computed(() => [{
icon: 'iconfont icon-github',
@ -55,21 +56,12 @@ const list = computed(() => [{
}]
}])
const updatePageTitle = () => setNavigationTitle('nav.help')
onShow(() => {
updatePageTitle()
})
onLocaleChange(updatePageTitle)
function handleText(item) {
uni.navigateTo({
url: `/pages/common/textview/index?title=${item.title}&content=${item.content}`
});
}
onUnmounted(() => {
offLocaleChange(updatePageTitle)
})
</script>
<style lang="scss" scoped>

@ -1,5 +1,6 @@
<template>
<view class="container">
<NavBar :title="$t('nav.editInfo')" />
<view class="example">
<uni-forms ref="form" :model="user" labelWidth="80px">
<uni-forms-item :label="$t('editInfo.nickname')" name="nickName">
@ -21,10 +22,14 @@
</template>
<script>
import NavBar from '@/components/common/NavBar.vue'
import { getUserProfile } from "@/api/system/user"
import { updateUserProfile } from "@/api/system/user"
export default {
components: {
NavBar
},
data() {
return {
user: {
@ -43,13 +48,9 @@ export default {
this.refreshI18n()
this.localeChangeHandler = () => {
this.refreshI18n()
this.setPageTitle()
}
uni.$on('app-locale-changed', this.localeChangeHandler)
},
onShow() {
this.setPageTitle()
},
onUnload() {
if (this.localeChangeHandler) {
uni.$off('app-locale-changed', this.localeChangeHandler)
@ -59,11 +60,6 @@ export default {
this.$refs.form.setRules(this.rules)
},
methods: {
setPageTitle() {
uni.setNavigationBarTitle({
title: this.$t('nav.editInfo')
})
},
refreshI18n() {
this.sexs = [{
text: this.$t('info.male'),

@ -1,5 +1,6 @@
<template>
<view class="container">
<NavBar :title="pageTitle" />
<uni-list>
<uni-list-item showExtraIcon="true" :extraIcon="{ type: 'contact' }" :title="t('info.username')" :rightText="user.email" />
<uni-list-item showExtraIcon="true" :extraIcon="{ type: 'person-filled' }" :title="t('info.nickname')" :rightText="user.nickname" />
@ -16,21 +17,17 @@
</template>
<script setup>
import { computed } from "vue";
import { getUserProfile } from "@/api/system/user"
import { onUnmounted, ref } from "vue";
import { ref } from "vue";
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import {timestampToTime} from "@/utils/dateUtil";
import { onLocaleChange, offLocaleChange, setNavigationTitle } from '@/locales'
import NavBar from '@/components/common/NavBar.vue'
const { t } = useI18n()
const user = ref({})
const updatePageTitle = () => setNavigationTitle('nav.info')
onShow(() => {
updatePageTitle()
})
onLocaleChange(updatePageTitle)
const pageTitle = computed(() => t('nav.info'))
function getUser() {
getUserProfile().then(response => {
@ -39,10 +36,6 @@ function getUser() {
}
getUser()
onUnmounted(() => {
offLocaleChange(updatePageTitle)
})
</script>
<style lang="scss">

@ -1,5 +1,6 @@
<template>
<view class="pwd-retrieve-container">
<NavBar :title="$t('nav.pwd')" />
<uni-forms ref="form" :value="user" labelWidth="80px">
<uni-forms-item required name="oldPassword" :label="$t('pwd.oldPassword')">
<uni-easyinput type="password" v-model="user.oldPassword" :placeholder="$t('pwd.oldPasswordPlaceholder')" />
@ -16,9 +17,13 @@
</template>
<script>
import NavBar from '@/components/common/NavBar.vue'
import { updateUserPwd } from "@/api/system/user"
export default {
components: {
NavBar
},
data() {
return {
user: {
@ -34,13 +39,9 @@
this.refreshI18n()
this.localeChangeHandler = () => {
this.refreshI18n()
this.setPageTitle()
}
uni.$on('app-locale-changed', this.localeChangeHandler)
},
onShow() {
this.setPageTitle()
},
onUnload() {
if (this.localeChangeHandler) {
uni.$off('app-locale-changed', this.localeChangeHandler)
@ -50,11 +51,6 @@
this.$refs.form.setRules(this.rules)
},
methods: {
setPageTitle() {
uni.setNavigationBarTitle({
title: this.$t('nav.pwd')
})
},
refreshI18n() {
this.rules = {
oldPassword: {

@ -1,5 +1,6 @@
<template>
<view class="setting-container" :style="{ height: `${windowHeight}px` }">
<NavBar :title="pageTitle" />
<view class="menu-list">
<view class="list-cell list-cell-arrow" @click="handleLanguageChange">
<view class="menu-item-box">
@ -45,31 +46,21 @@
</template>
<script setup>
import { computed, onUnmounted, ref } from "vue";
import { onShow } from '@dcloudio/uni-app'
import { computed, ref } from "vue";
import { useI18n } from 'vue-i18n'
import useUserStore from '@/store/modules/user'
import { getCurrentLocale, setLocale, onLocaleChange, offLocaleChange, setNavigationTitle } from '@/locales'
import { getCurrentLocale, setLocale } from '@/locales'
import NavBar from '@/components/common/NavBar.vue'
const userStore = useUserStore()
const { t } = useI18n()
const pageTitle = computed(() => t('nav.setting'))
const windowHeight = ref(uni.getSystemInfoSync().windowHeight);
const popup = ref(null);
const currentLocale = ref(getCurrentLocale())
const currentLanguageLabel = computed(() => currentLocale.value === 'en-US' ? t('setting.enUS') : t('setting.zhCN'))
const updatePageTitle = () => setNavigationTitle('nav.setting')
onShow(() => {
updatePageTitle()
})
const updateLocale = (locale) => {
currentLocale.value = locale
updatePageTitle()
}
onLocaleChange(updateLocale)
function handleToPwd() {
uni.navigateTo({
url: '/pages_mine/pages/pwd/index'
@ -121,9 +112,6 @@ function dialogConfirm() {
function dialogClose() {
};
onUnmounted(() => {
offLocaleChange(updateLocale)
})
</script>
<style lang="scss" scoped>

@ -23,7 +23,7 @@ export default {
},
/**
* tabBar tabBar
* tabbar 使 reLaunch switchTab
* @param url
* @returns
*/
@ -32,7 +32,7 @@ export default {
console.log(!!params?url + '?' + tansParams(params):url);
return new Promise((resolve, reject) => {
uni.switchTab({
uni.reLaunch({
url: !!params?url + '?' + tansParams(params):url,
success: resolve,
fail: reject

@ -3,7 +3,6 @@ import { getToken, setToken, removeToken } from "@/utils/auth";
import defAva from "@/static/images/profile.jpg";
import { defineStore } from "pinia";
import { initAllDict } from "@/utils/dict";
import { syncTabBarMenus } from "@/utils/permissionMenu";
export interface LoginForm {
username: string;
@ -65,7 +64,6 @@ const useUserStore = defineStore("user", {
}
this.permissions = res.data.permissions;
this.menus = Array.isArray(res.data.menus) ? res.data.menus : [];
syncTabBarMenus(this.menus);
this.name = user.nickname;
this.userId = user.id;
this.avatar = avatar;

@ -1,48 +0,0 @@
const DEFAULT_NAV_BACKGROUND = '#22486e'
const DEFAULT_NAV_FRONT = '#ffffff'
const LOGIN_NAV_BACKGROUND = '#ffffff'
const LOGIN_NAV_FRONT = '#000000'
function getCurrentRoute() {
try {
const pages = getCurrentPages()
if (!pages || pages.length === 0) return ''
return pages[pages.length - 1]?.route || ''
} catch (error) {
return ''
}
}
function getNavigationTheme(route = '') {
const currentRoute = route || getCurrentRoute()
if (currentRoute === 'pages/login') {
return {
frontColor: LOGIN_NAV_FRONT,
backgroundColor: LOGIN_NAV_BACKGROUND
}
}
return {
frontColor: DEFAULT_NAV_FRONT,
backgroundColor: DEFAULT_NAV_BACKGROUND
}
}
export function applyNavigationBarTheme(route = '') {
const theme = getNavigationTheme(route)
try {
uni.setNavigationBarColor({
frontColor: theme.frontColor,
backgroundColor: theme.backgroundColor,
animation: {
duration: 0,
timingFunc: 'linear'
}
})
} catch (error) {
}
}
export function getDefaultNavigationBackground() {
return DEFAULT_NAV_BACKGROUND
}

@ -64,6 +64,19 @@ function toArray(value) {
return Array.isArray(value) ? value : []
}
function walkMenus(menus, visitor, parent = null) {
toArray(menus).forEach((menu, index) => {
if (!menu || typeof menu !== 'object') {
return
}
visitor(menu, parent, index)
const children = toArray(menu.children)
if (children.length > 0) {
walkMenus(children, visitor, menu)
}
})
}
function normalizeMenuKey(value) {
return String(value || '')
.trim()
@ -112,6 +125,42 @@ export function getTopLevelMenus(menus) {
return toArray(menus).filter((menu) => menu && typeof menu === 'object')
}
export function flattenMenus(menus) {
const result = []
walkMenus(menus, (menu, parent, index) => {
result.push({
...menu,
_parent: parent || null,
_levelIndex: index
})
})
return result
}
export function getConfigurableNavMenus(menus) {
const dedupe = new Set()
return flattenMenus(menus).filter((menu) => {
if (menu.type !== 2 || menu.id == null || dedupe.has(menu.id)) {
return false
}
dedupe.add(menu.id)
return true
})
}
export function buildNavMenuViewModels(menus) {
return getConfigurableNavMenus(menus).map((menu, index) => {
const displayName = String(menu.name || menu.enName || '').trim() || `菜单${index + 1}`
return {
...menu,
displayName,
route: resolveMenuUrl(menu),
symbol: getMenuSymbol(displayName, index),
accentColor: getModuleColor(index)
}
})
}
export function getDynamicTabMenus(menus) {
return getTopLevelMenus(menus).filter((menu) => !looksLikeHomeMenu(menu))
}
@ -167,17 +216,10 @@ export function getMenuSymbol(name, index) {
export function syncTabBarMenus(menus, options = {}) {
const dynamicMenus = getDynamicTabMenus(menus)
const labels = [
return [
options.homeText || '首页',
dynamicMenus[0]?.name || options.reportFallback || '报表',
dynamicMenus[1]?.name || dynamicMenus[0]?.name || options.workFallback || '管理',
options.mineText || '我的'
]
labels.forEach((text, index) => {
uni.setTabBarItem({
index,
text
})
})
}
Loading…
Cancel
Save