|
|
@@ -35,7 +35,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
|
|
+import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
|
|
|
import { useRouter } from 'vue-router'
|
|
|
import { DEFAULT_BACKEND_HOME } from '@/router'
|
|
|
import useUserStore from '@/store/modules/user'
|
|
|
@@ -61,11 +61,26 @@ const modulesCount = ref(7)
|
|
|
let currentAngle = ref(0)
|
|
|
const ANIMATION_DURATION = 500
|
|
|
const ROTATE_INTERVAL = 5000
|
|
|
-const STEP_ANGLE = (2 * Math.PI) / 7
|
|
|
+// 每步旋转角度按实际模块数动态计算,避免去重后模块数量变化导致错位
|
|
|
+const stepAngle = computed(() => (2 * Math.PI) / Math.max(modulesCount.value, 1))
|
|
|
|
|
|
// 动态模块(从路由获取)
|
|
|
const modules = ref([])
|
|
|
|
|
|
+const resolveModuleIcon = (route) => {
|
|
|
+ const identity = [route.name, route.path, route.meta?.title].filter(Boolean).join(' ').toLowerCase()
|
|
|
+ const iconMappings = [
|
|
|
+ { pattern: /生命线|lifecompany|citylifeline/, iconClass: 'icon-operation' },
|
|
|
+ { pattern: /应急|指挥|dispatch/, iconClass: 'icon-sewage' },
|
|
|
+ { pattern: /管网设施|pipenetwork/, iconClass: 'icon-bi' },
|
|
|
+ { pattern: /供水|water/, iconClass: 'icon-flood' },
|
|
|
+ { pattern: /燃气|gas/, iconClass: 'icon-water' },
|
|
|
+ { pattern: /排水|drainage/, iconClass: 'icon-123' },
|
|
|
+ { pattern: /窨井盖|manhole/, iconClass: 'icon-ps' }
|
|
|
+ ]
|
|
|
+ return iconMappings.find(({ pattern }) => pattern.test(identity))?.iconClass
|
|
|
+}
|
|
|
+
|
|
|
// 【优化1】缓存模块样式,避免频繁计算
|
|
|
const moduleStyles = reactive({})
|
|
|
|
|
|
@@ -127,7 +142,7 @@ let animationStartTime = 0
|
|
|
const startRotateAnimation = () => {
|
|
|
if (isPaused.value) return
|
|
|
startAngle = currentAngle.value
|
|
|
- targetAngle = currentAngle.value + STEP_ANGLE
|
|
|
+ targetAngle = currentAngle.value + stepAngle.value
|
|
|
animationStartTime = performance.now()
|
|
|
animate()
|
|
|
}
|
|
|
@@ -202,26 +217,28 @@ function updateParentPath(routes) {
|
|
|
// 加载动态驾驶舱
|
|
|
// ==============================================
|
|
|
const loadDynamicModules = () => {
|
|
|
- const subSystemRoutes = permissionStore.sidebarRouters.filter(route =>
|
|
|
- route.name
|
|
|
- )
|
|
|
+ // 用 routes(仅 generateRoutes 时写入)而非 sidebarRouters:
|
|
|
+ // sidebarRouters 会被 TopNav/主题设置覆盖成变换后的数据,SPA 内返回门户会读到脏数据
|
|
|
+ const subSystemRoutes = permissionStore.routes.filter(route => route.name && !route.hidden)
|
|
|
const newRoutes = updateParentPath(subSystemRoutes)
|
|
|
- const iconList = ['icon-operation', 'icon-sewage', 'icon-bi', 'icon-water', 'icon-flood', 'icon-123','icon-ps']
|
|
|
- const res = newRoutes.map((item, i) => ({
|
|
|
- name: item.meta?.title || '驾驶舱',
|
|
|
- iconClass: iconList[i] || 'icon-flood',
|
|
|
- path: item.path || '/'
|
|
|
- }))
|
|
|
-
|
|
|
- while (res.length < 6) {
|
|
|
+ // 按名称去重:sys_menu 中重复配置的同名子系统目录只展示一次
|
|
|
+ const seenTitles = new Set()
|
|
|
+ const res = []
|
|
|
+ newRoutes.forEach(item => {
|
|
|
+ const name = item.meta?.title || '驾驶舱'
|
|
|
+ const iconClass = resolveModuleIcon(item)
|
|
|
+ // 素材内嵌了子系统名称,未知路由不能使用其他系统的素材以免标题与入口不符。
|
|
|
+ if (!iconClass) return
|
|
|
+ if (seenTitles.has(name)) return
|
|
|
+ seenTitles.add(name)
|
|
|
res.push({
|
|
|
- name: `驾驶舱${res.length + 1}`,
|
|
|
- iconClass: 'icon-flood',
|
|
|
- path: '/'
|
|
|
+ name,
|
|
|
+ iconClass,
|
|
|
+ path: item.path || '/'
|
|
|
})
|
|
|
- }
|
|
|
- modules.value = res.slice(0, 7)
|
|
|
- modulesCount.value = modules.value.length
|
|
|
+ })
|
|
|
+ modules.value = res
|
|
|
+ modulesCount.value = res.length
|
|
|
updateAllModuleStyles()
|
|
|
}
|
|
|
|