Browse Source

chore: commit pending shell map proxy and brand assets

Persist leftover working-tree changes already used by local/prod builds:
dev proxy to remote gateway, MapLayer HMR-safe singleton map, commonRefs/
mapHelper singletons, SideMenu logo, TDesign CSS load order, prototype
style updates, favicon/logo/monitor icons, and operation-log cell markup.
main
xiaosi 3 weeks ago
parent
commit
7efe68997b
  1. 1
      index.html
  2. 4
      public/assets/icons/dock-marker.svg
  3. 5
      public/assets/icons/dock.svg
  4. 9
      public/assets/icons/drone-marker.svg
  5. 10
      public/assets/icons/drone.svg
  6. BIN
      public/assets/login-hero-dock-drone.png
  7. BIN
      public/assets/logo.png
  8. BIN
      public/favicon.ico
  9. 6
      src/core/mapHelper.js
  10. 138
      src/layout/MapLayer.vue
  11. 35
      src/layout/components/SideMenu.vue
  12. 3
      src/main.js
  13. 2013
      src/styles/prototype.css
  14. 6
      src/utils/commonRefs.js
  15. 2
      src/views/OperationLogsView/OperationLogsView.vue
  16. 5
      vite.config.js

1
index.html

@ -3,6 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" />
<title>嘉谷低空智控平台</title> <title>嘉谷低空智控平台</title>
</head> </head>
<body> <body>

4
public/assets/icons/dock-marker.svg

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 56" fill="none">
<path d="M24 54s16-14.8 16-28A16 16 0 1 0 8 26c0 13.2 16 28 16 28Z" fill="#1F6FBF" stroke="#FFFFFF" stroke-width="2.5"/>
<path d="M16.2 24.2 24 17.4l7.8 6.8V33a1.2 1.2 0 0 1-1.2 1.2h-3.4v-4.4h-5.2V34.2h-3.4A1.2 1.2 0 0 1 16.2 33V24.2Z" fill="#FFFFFF"/>
</svg>

After

Width:  |  Height:  |  Size: 337 B

5
public/assets/icons/dock.svg

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<rect x="2.5" y="2.5" width="19" height="19" rx="5" fill="#E8F5EF"/>
<path d="M7 11.2 12 6.8l5 4.4V17a1 1 0 0 1-1 1h-2.4v-3.4h-3.2V18H8a1 1 0 0 1-1-1v-5.8Z" stroke="#1F7A57" stroke-width="1.6" stroke-linejoin="round"/>
<path d="M9.6 11.6h4.8" stroke="#1F7A57" stroke-width="1.6" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 390 B

9
public/assets/icons/drone-marker.svg

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 56" fill="none">
<path d="M24 54s16-14.8 16-28A16 16 0 1 0 8 26c0 13.2 16 28 16 28Z" fill="#1F9B6A" stroke="#FFFFFF" stroke-width="2.5"/>
<circle cx="16.2" cy="18.4" r="2.4" stroke="#FFFFFF" stroke-width="1.8"/>
<circle cx="31.8" cy="18.4" r="2.4" stroke="#FFFFFF" stroke-width="1.8"/>
<circle cx="16.2" cy="31.8" r="2.4" stroke="#FFFFFF" stroke-width="1.8"/>
<circle cx="31.8" cy="31.8" r="2.4" stroke="#FFFFFF" stroke-width="1.8"/>
<path d="M18.2 20.4 21.4 23.6M29.8 20.4 26.6 23.6M18.2 29.8 21.4 26.6M29.8 29.8 26.6 26.6" stroke="#FFFFFF" stroke-width="1.6" stroke-linecap="round"/>
<rect x="20.2" y="22.2" width="7.6" height="7.6" rx="1.8" fill="#FFFFFF"/>
</svg>

After

Width:  |  Height:  |  Size: 738 B

10
public/assets/icons/drone.svg

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<rect x="2.5" y="2.5" width="19" height="19" rx="5" fill="#EAF3FB"/>
<circle cx="6.4" cy="6.4" r="1.9" stroke="#2B6FBE" stroke-width="1.55"/>
<circle cx="17.6" cy="6.4" r="1.9" stroke="#2B6FBE" stroke-width="1.55"/>
<circle cx="6.4" cy="17.6" r="1.9" stroke="#2B6FBE" stroke-width="1.55"/>
<circle cx="17.6" cy="17.6" r="1.9" stroke="#2B6FBE" stroke-width="1.55"/>
<path d="M8 8 10.5 10.5M16 8 13.5 10.5M8 16 10.5 13.5M16 16 13.5 13.5" stroke="#2B6FBE" stroke-width="1.55" stroke-linecap="round"/>
<rect x="9.4" y="9.4" width="5.2" height="5.2" rx="1.3" stroke="#2B6FBE" stroke-width="1.55"/>
<circle cx="12" cy="12" r="0.95" fill="#2B6FBE"/>
</svg>

After

Width:  |  Height:  |  Size: 739 B

BIN
public/assets/login-hero-dock-drone.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 6.4 MiB

BIN
public/assets/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
public/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

6
src/core/mapHelper.js

@ -72,4 +72,8 @@ class MapHelper {
} }
} }
export default new MapHelper()
const g = typeof globalThis !== 'undefined' ? globalThis : undefined
const singleton = (g && g.__LAIC_MAP_HELPER__) || new MapHelper()
if (g) g.__LAIC_MAP_HELPER__ = singleton
export default singleton

138
src/layout/MapLayer.vue

@ -4,9 +4,55 @@ import mapboxgl from 'mapbox-gl'
import mapConfig, { MAP_STYLES, MAP_OVERVIEW, MAP_VIEW } from '@/config/map' import mapConfig, { MAP_STYLES, MAP_OVERVIEW, MAP_VIEW } from '@/config/map'
import commonRefs from '@/utils/commonRefs' import commonRefs from '@/utils/commonRefs'
import mapHelper from '@/core/mapHelper' import mapHelper from '@/core/mapHelper'
import { applyChineseLabels } from '@/utils/mapLabels'
import 'mapbox-gl/dist/mapbox-gl.css' import 'mapbox-gl/dist/mapbox-gl.css'
// Map DOM/window HMR/ Monitor
if (!mapboxgl.Map.__laicPatched) {
const OriginalMap = mapboxgl.Map
class LaicMap extends OriginalMap {
constructor(options) {
super(options)
try {
const el = this.getContainer?.()
if (el) el.__laicMap = this
if (typeof window !== 'undefined') {
window.__LAIC_MAPBOX_MAP__ = this
window.dispatchEvent(new CustomEvent('laic-map-ready', { detail: this }))
}
} catch (_) {
/* ignore */
}
}
}
LaicMap.__laicPatched = true
mapboxgl.Map = LaicMap
}
let map = null let map = null
let resizeObserver = null
function resizeMap(target = map) {
if (!target) return
try {
target.resize()
} catch (_) {
/* ignore */
}
}
function bindResizeObserver(target) {
if (resizeObserver) {
resizeObserver.disconnect()
resizeObserver = null
}
const el = target?.getContainer?.()
if (!el || typeof ResizeObserver === 'undefined') return
resizeObserver = new ResizeObserver(() => {
resizeMap(target)
})
resizeObserver.observe(el)
}
function ensureGlobe(target) { function ensureGlobe(target) {
if (!target) return if (!target) return
@ -24,6 +70,62 @@ function clearMapRef(target) {
mapHelper.setMap(null) mapHelper.setMap(null)
commonRefs.removeRef('map') commonRefs.removeRef('map')
} }
if (typeof window !== 'undefined' && window.__LAIC_MAPBOX_MAP__ === target) {
delete window.__LAIC_MAPBOX_MAP__
}
try {
if (target.getContainer?.().__laicMap === target) delete target.getContainer().__laicMap
} catch (_) {
/* ignore */
}
}
function exposeMap(target) {
if (!target) return
const el =
(typeof document !== 'undefined' ? document.getElementById('map') : null) ||
target.getContainer?.()
if (el) {
try {
Object.defineProperty(el, '__laicMap', { value: target, writable: true, configurable: true })
} catch (_) {
el.__laicMap = target
}
}
if (typeof window !== 'undefined') {
try {
Object.defineProperty(window, '__LAIC_MAPBOX_MAP__', {
value: target,
writable: true,
configurable: true,
})
} catch (_) {
window.__LAIC_MAPBOX_MAP__ = target
}
window.dispatchEvent(new CustomEvent('laic-map-ready', { detail: target }))
}
commonRefs.setRef('map', target)
mapHelper.setMap(target)
}
function destroyOrphanMap(container) {
if (!container) return
// HMR / Mapbox new Map
const orphan = container.__laicMap
if (orphan && typeof orphan.remove === 'function') {
try {
orphan.remove()
} catch (_) {
/* ignore */
}
}
delete container.__laicMap
// Mapbox mapboxgl-map
if (container.classList?.contains('mapboxgl-map')) {
container.innerHTML = ''
container.classList.remove('mapboxgl-map')
container.removeAttribute('style')
}
} }
function initMap() { function initMap() {
@ -35,9 +137,13 @@ function initMap() {
return return
} }
const container =
(typeof document !== 'undefined' ? document.getElementById('map') : null) || 'map'
if (typeof container !== 'string') destroyOrphanMap(container)
mapboxgl.accessToken = tk mapboxgl.accessToken = tk
map = new mapboxgl.Map({ map = new mapboxgl.Map({
container: 'map',
container,
style: MAP_STYLES.satellite, style: MAP_STYLES.satellite,
center: MAP_OVERVIEW.center, center: MAP_OVERVIEW.center,
zoom: MAP_OVERVIEW.zoom, zoom: MAP_OVERVIEW.zoom,
@ -46,11 +152,29 @@ function initMap() {
projection: 'globe', projection: 'globe',
}) })
if (map.isStyleLoaded()) ensureGlobe(map)
else map.once('style.load', () => ensureGlobe(map))
exposeMap(map)
console.info('[MapLayer] map ready', {
hasDom: !!(typeof document !== 'undefined' && document.getElementById('map')?.__laicMap),
hasWin: !!(typeof window !== 'undefined' && window.__LAIC_MAPBOX_MAP__),
})
if (map.isStyleLoaded()) {
ensureGlobe(map)
applyChineseLabels(map)
} else {
map.once('style.load', () => {
ensureGlobe(map)
applyChineseLabels(map)
})
}
commonRefs.setRef('map', map)
mapHelper.setMap(map)
// Mapbox ~300px
map.once('load', () => resizeMap(map))
requestAnimationFrame(() => {
resizeMap(map)
requestAnimationFrame(() => resizeMap(map))
})
bindResizeObserver(map)
} }
onMounted(() => { onMounted(() => {
@ -64,6 +188,10 @@ onMounted(() => {
}) })
onUnmounted(() => { onUnmounted(() => {
if (resizeObserver) {
resizeObserver.disconnect()
resizeObserver = null
}
if (map) { if (map) {
const dying = map const dying = map
map = null map = null

35
src/layout/components/SideMenu.vue

@ -81,7 +81,7 @@ async function logout() {
<template> <template>
<div :class="s.root"> <div :class="s.root">
<div :class="s.brand"> <div :class="s.brand">
<div :class="s.brandMark"><span /><span /><span /></div>
<img :class="s.brandMark" src="/assets/logo.png" alt="嘉谷" />
<div> <div>
<strong>嘉谷</strong> <strong>嘉谷</strong>
<small>低空智控平台</small> <small>低空智控平台</small>
@ -227,15 +227,8 @@ async function logout() {
.brandMark { .brandMark {
width: 28px; width: 28px;
height: 28px; height: 28px;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 3px;
span {
display: block;
border-radius: 2px;
background: #7db5f5;
}
object-fit: contain;
flex-shrink: 0;
} }
.menu { .menu {
@ -291,6 +284,13 @@ async function logout() {
background: rgba(255, 255, 255, 0.06); background: rgba(255, 255, 255, 0.06);
} }
:global(.t-button__text) {
width: 100%;
display: flex;
align-items: center;
gap: 10px;
color: inherit;
> span { > span {
width: 30px; width: 30px;
height: 30px; height: 30px;
@ -304,6 +304,16 @@ async function logout() {
flex-shrink: 0; flex-shrink: 0;
} }
> div {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
}
strong { strong {
display: block; display: block;
font-size: 12px; font-size: 12px;
@ -315,11 +325,6 @@ async function logout() {
color: #8fa0ad; color: #8fa0ad;
font-size: 10px; font-size: 10px;
} }
div {
flex: 1;
min-width: 0;
}
} }
.chevron { .chevron {

3
src/main.js

@ -1,8 +1,9 @@
import './styles/prototype.css'
import 'tdesign-vue-next/es/style/css.mjs'
import { createApp } from 'vue' import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import { setupStore } from './stores' import { setupStore } from './stores'
import './styles/prototype.css'
import 'tdesign-vue-next/es/message/style/css.mjs' import 'tdesign-vue-next/es/message/style/css.mjs'
import 'tdesign-vue-next/es/dialog/style/css.mjs' import 'tdesign-vue-next/es/dialog/style/css.mjs'
import 'tdesign-vue-next/es/button/style/css.mjs' import 'tdesign-vue-next/es/button/style/css.mjs'

2013
src/styles/prototype.css

File diff suppressed because one or more lines are too long

6
src/utils/commonRefs.js

@ -57,4 +57,8 @@ class CommonRefs {
} }
} }
export default new CommonRefs()
const g = typeof globalThis !== 'undefined' ? globalThis : undefined
const singleton = (g && g.__LAIC_COMMON_REFS__) || new CommonRefs()
if (g) g.__LAIC_COMMON_REFS__ = singleton
export default singleton

2
src/views/OperationLogsView/OperationLogsView.vue

@ -34,7 +34,7 @@
empty="暂无设备操作记录" empty="暂无设备操作记录"
> >
<template #command="{ row }"> <template #command="{ row }">
<strong>{{ row.commandName }}</strong><small>{{ row.commandType }}</small>
<div class="command-cell"><strong>{{ row.commandName }}</strong><small>{{ row.commandType }}</small></div>
</template> </template>
<template #status="{ row }"> <template #status="{ row }">
<span class="table-status" :class="row.statusClass"><i></i>{{ row.statusName }}</span> <span class="table-status" :class="row.statusClass"><i></i>{{ row.statusName }}</span>

5
vite.config.js

@ -30,12 +30,11 @@ export default defineConfig({
host: '0.0.0.0', host: '0.0.0.0',
port: 5173, port: 5173,
proxy: { proxy: {
// 浏览器请求 /api/* → 后端 /*(去掉 /api 前缀)
// 浏览器请求 /api/* → http://119.3.111.201/api/*(远程网关保留 /api 前缀)
'/api': { '/api': {
target: 'http://192.168.10.29:8080',
target: 'http://119.3.111.201',
changeOrigin: true, changeOrigin: true,
agent: proxyAgent, agent: proxyAgent,
rewrite: (path) => path.replace(/^\/api/, ''),
}, },
}, },
}, },

Loading…
Cancel
Save