From 2c5ae7c42f89d627fc45651fe3052c2a43a0f730 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=E5=85=88=E7=94=9F?= <1211120248@qq.com>
Date: Thu, 9 Jul 2026 17:16:17 +0800
Subject: [PATCH] fix: refine screen issue acceptance updates
---
src/config/layers.js | 4 +-
src/services/homeVideoApi.js | 25 ++-
src/views/forestGrassWet/map.js | 58 ++++--
src/views/gdMap/map.js | 38 +++-
src/views/grassLivestockBalance/index.vue | 211 ++++++++++++++++++++--
src/views/home/index.vue | 122 ++++++++++---
src/views/pastureManagement/index.vue | 114 +++++++++---
src/views/yakManagement/YakOlMap.vue | 18 +-
src/views/yakManagement/data.js | 38 +++-
src/views/yakManagement/index.vue | 41 +++--
10 files changed, 552 insertions(+), 117 deletions(-)
diff --git a/src/config/layers.js b/src/config/layers.js
index db5cea6..d72ad29 100644
--- a/src/config/layers.js
+++ b/src/config/layers.js
@@ -15,11 +15,11 @@ export const SATELLITE_TEXTURE_ZOOM = 9
export const NATIONAL_PARK_ZONING_COLORS = {
core: {
fillColor: "#24F6C4",
- strokeColor: "#B7FFF2",
+ strokeColor: "#FFB15E",
},
general: {
fillColor: "#FFE06B",
- strokeColor: "#FFF2A0",
+ strokeColor: "#FF8F3D",
},
}
diff --git a/src/services/homeVideoApi.js b/src/services/homeVideoApi.js
index c1c2fb6..8b3410b 100644
--- a/src/services/homeVideoApi.js
+++ b/src/services/homeVideoApi.js
@@ -14,6 +14,10 @@ const HONGYUAN_TOWN_NAMES = [
"麦洼乡",
"色地镇",
]
+const MONITORING_FALLBACK_ROWS = [
+ { id: "fallback-qiongxi-pasture", name: "邛溪镇家庭生态牧场监控", number: "CAM-001", statusName: "预览点位" },
+ { id: "fallback-wache-grassland", name: "瓦切镇草场监控", number: "CAM-002", statusName: "预览点位" },
+]
function joinBase(path) {
if (/^https?:\/\//.test(path)) return path
@@ -139,11 +143,18 @@ function normalizeMonitoring(row, index, total, validTotal) {
}
export async function getHomeMonitorings(params = {}) {
- const data = await requestJson(joinBase("/api/home/monitorings"), {
- params,
- timeout: 18000,
- }).then(unwrapResponse)
- const rows = Array.isArray(data) ? data : data?.rows || data?.list || []
- const playableRows = rows.filter(isPlayableMonitoring)
- return playableRows.map((row, index) => normalizeMonitoring(row, index, rows.length, playableRows.length))
+ try {
+ const data = await requestJson(joinBase("/api/home/monitorings"), {
+ params,
+ timeout: 18000,
+ }).then(unwrapResponse)
+ const rows = Array.isArray(data) ? data : data?.rows || data?.list || []
+ const playableRows = rows.filter(isPlayableMonitoring)
+ if (playableRows.length) {
+ return playableRows.map((row, index) => normalizeMonitoring(row, index, rows.length, playableRows.length))
+ }
+ } catch (error) {
+ console.warn("[homeVideo] fallback monitorings used", error)
+ }
+ return MONITORING_FALLBACK_ROWS.map((row, index) => normalizeMonitoring(row, index, MONITORING_FALLBACK_ROWS.length, 0))
}
diff --git a/src/views/forestGrassWet/map.js b/src/views/forestGrassWet/map.js
index b8b9a98..66b8292 100644
--- a/src/views/forestGrassWet/map.js
+++ b/src/views/forestGrassWet/map.js
@@ -96,6 +96,11 @@ function toFiniteNumber(value, fallback = 0) {
const number = Number(value)
return Number.isFinite(number) ? number : fallback
}
+function clampNumber(value, min, max) {
+ const number = Number(value)
+ if (!Number.isFinite(number)) return min
+ return Math.max(min, Math.min(max, number))
+}
function escapeHtml(value) {
return String(value ?? "").replace(/[&<>"']/g, (char) => ({
"&": "&",
@@ -1592,20 +1597,29 @@ export class World extends Mini3d {
const baseStyle = this.getVectorFeatureStyle(layer, feature)
this.drawVectorFeature(context, feature.geometry, {
...baseStyle,
- fill: "#001D2C",
- stroke: "#001D2C",
+ fill: "#351A05",
+ stroke: "#351A05",
polygonAlpha: 0,
- lineAlpha: 0.58,
- lineWidth: 12,
+ lineAlpha: 0.52,
+ lineWidth: 9,
glow: true,
})
this.drawVectorFeature(context, feature.geometry, {
...baseStyle,
- fill: "#F8FFFF",
- stroke: "#F8FFFF",
+ fill: "#FFE3AA",
+ stroke: "#FFE3AA",
polygonAlpha: 0,
- lineAlpha: 0.96,
- lineWidth: 3.2,
+ lineAlpha: 0.98,
+ lineWidth: 2.8,
+ glow: false,
+ })
+ this.drawVectorFeature(context, feature.geometry, {
+ ...baseStyle,
+ fill: "#FF8F3D",
+ stroke: "#FF8F3D",
+ polygonAlpha: 0,
+ lineAlpha: 0.9,
+ lineWidth: 1.45,
glow: false,
})
})
@@ -2332,11 +2346,11 @@ export class World extends Mini3d {
return {
fill: legend.fillColor || "#30DCFF",
stroke: legend.strokeColor || "#F2FFFF",
- polygonAlpha: 0.36,
- lineAlpha: 1,
- lineWidth: 5.8,
+ polygonAlpha: 0.28,
+ lineAlpha: 0.95,
+ lineWidth: 3.2,
pointRadius: 4.5,
- glow: true,
+ glow: false,
}
}
if (layer?.boundaryOnly) {
@@ -3916,6 +3930,8 @@ export class World extends Mini3d {
label.setParent(labelGroup)
label.userData = {
...province,
+ labelType: "town-label",
+ baseScale: 0.018,
townName: province.name,
}
return label
@@ -5361,10 +5377,26 @@ export class World extends Mini3d {
geoProjection(args) {
return geoMercator().center(this.geoProjectionCenter).scale(this.geoProjectionScale).translate([0, 0])(args)
- }
+ }
+ updateTownLabelScale() {
+ const labels = this.townNameLabels || this.otherLabel || []
+ const camera = this.camera?.instance
+ const target = this.camera?.controls?.target
+ if (!labels.length || !camera || !target) return
+ const distance = camera.position.distanceTo(target)
+ const scale = clampNumber(distance * 0.00072, 0.011, 0.026)
+ const opacity = distance > 45 ? "0.76" : distance < 22 ? "0.94" : "1"
+ labels.forEach((label) => {
+ if (label?.userData?.labelType !== "town-label") return
+ label.scale.set(scale, scale, scale)
+ const element = label.element?.querySelector?.(".other-label")
+ if (element) element.style.opacity = opacity
+ })
+ }
update(delta) {
super.update(delta)
this.updateDynamicSatelliteTiles()
+ this.updateTownLabelScale()
this.updateTradeFlowPulses(delta)
this.interactionManager && this.interactionManager.update()
}
diff --git a/src/views/gdMap/map.js b/src/views/gdMap/map.js
index cdf906d..8900d22 100644
--- a/src/views/gdMap/map.js
+++ b/src/views/gdMap/map.js
@@ -71,6 +71,11 @@ function normalizeMapGeoJsonData(data) {
})
return parsed
}
+function clampNumber(value, min, max) {
+ const number = Number(value)
+ if (!Number.isFinite(number)) return min
+ return Math.max(min, Math.min(max, number))
+}
export class World extends Mini3d {
constructor(canvas, assets, options = {}) {
super(canvas)
@@ -965,9 +970,14 @@ export class World extends Mini3d {
new Vector3(x, -y, self.depth + 0.85)
)
label3d.setLabelStyle(label, 0.018, "x")
- label.setParent(labelGroup)
- return label
- }
+ label.setParent(labelGroup)
+ label.userData = {
+ ...province,
+ labelType: "town-label",
+ baseScale: 0.018,
+ }
+ return label
+ }
function labelStyle03(data, label3d, labelGroup) {
let label = label3d.create("", `decoration-label ${data.reflect ? " reflect" : ""}`, false)
const [x, y] = self.geoProjection(data.center)
@@ -1322,11 +1332,27 @@ export class World extends Mini3d {
})
}
- geoProjection(args) {
- return geoMercator().center(this.geoProjectionCenter).scale(this.geoProjectionScale).translate([0, 0])(args)
- }
+ geoProjection(args) {
+ return geoMercator().center(this.geoProjectionCenter).scale(this.geoProjectionScale).translate([0, 0])(args)
+ }
+ updateTownLabelScale() {
+ const labels = this.otherLabel || []
+ const camera = this.camera?.instance
+ const target = this.camera?.controls?.target
+ if (!labels.length || !camera || !target) return
+ const distance = camera.position.distanceTo(target)
+ const scale = clampNumber(distance * 0.00072, 0.011, 0.026)
+ const opacity = distance > 45 ? "0.76" : distance < 22 ? "0.94" : "1"
+ labels.forEach((label) => {
+ if (label?.userData?.labelType !== "town-label") return
+ label.scale.set(scale, scale, scale)
+ const element = label.element?.querySelector?.(".other-label")
+ if (element) element.style.opacity = opacity
+ })
+ }
update() {
super.update()
+ this.updateTownLabelScale()
this.interactionManager && this.interactionManager.update()
}
pauseScene() {
diff --git a/src/views/grassLivestockBalance/index.vue b/src/views/grassLivestockBalance/index.vue
index ab49b32..22eda81 100644
--- a/src/views/grassLivestockBalance/index.vue
+++ b/src/views/grassLivestockBalance/index.vue
@@ -204,6 +204,27 @@
+
+
+
+ 超载率排行
+ 乡镇 Top10
+
+
+
+
{{ item.rank }}
+
{{ item.name }}
+
+
{{ formatPercent(item.overloadRate) }}
+
+
+
@@ -437,6 +458,36 @@ const workflowRows = computed(() => [
])
const activeAdviceRows = computed(() => analysisDone.value ? splitAdvice(activeBalance.value, activeDerived.value.risk) : [])
+const overloadRankSourceRows = computed(() =>
+ townRows.value
+ .filter((item) => Number.isFinite(Number(item.overloadRate)))
+ .slice()
+ .sort((a, b) => Number(b.overloadRate) - Number(a.overloadRate))
+ .slice(0, 10)
+)
+const overloadRankMax = computed(() => Math.max(
+ 1,
+ ...overloadRankSourceRows.value.map((item) => Math.max(0, Number(item.overloadRate) || 0)),
+))
+const overloadRankRows = computed(() =>
+ overloadRankSourceRows.value.map((item, index) => ({
+ ...item,
+ rank: index + 1,
+ }))
+)
+
+function getOverloadRankProgress(item) {
+ const rate = Math.max(0, Number(item?.overloadRate) || 0)
+ return Math.max(6, Math.min(100, (rate / overloadRankMax.value) * 100))
+}
+
+function getOverloadRankClass(item) {
+ const rate = Number(item?.overloadRate)
+ if (!Number.isFinite(rate)) return "is-empty"
+ if (rate >= 2.5) return "is-alert"
+ if (rate >= 2) return "is-warning"
+ return "is-stable"
+}
function updateScreenViewportHeight() {
screenViewportHeight.value = typeof window === "undefined" ? 1080 : window.innerHeight
@@ -509,8 +560,9 @@ function startAnalysis() {
let requestFailed = false
const analysisRequest = refreshActiveBalance().catch((error) => {
requestFailed = true
- analysisError.value = error?.message || "未查询到该对象的草畜平衡结果"
- return null
+ console.warn("[grassLivestockBalance] fallback balance row used", error)
+ analysisError.value = "接口暂不可用,已使用本地兜底口径"
+ return activeBalance.value
})
activeAnalysisRequest = analysisRequest
analysisTimer = window.setInterval(() => {
@@ -530,12 +582,15 @@ function startAnalysis() {
if (destroyed || analysisState.value !== "running" || activeAnalysisRequest !== analysisRequest) return
activeAnalysisRequest = null
clearAnalysisTimer()
- if (requestFailed || !apiResults.value[activeBalance.value?.key]) {
+ if (!activeBalance.value) {
analysisProgress.value = 0
processStageIndex.value = 0
analysisState.value = "idle"
return
}
+ if (!requestFailed && !apiResults.value[activeBalance.value?.key]) {
+ analysisError.value = "未查询到接口结果,已使用本地兜底口径"
+ }
analysisProgress.value = 100
processStageIndex.value = analysisProcessRows.value.length - 1
analysisState.value = "done"
@@ -1997,13 +2052,14 @@ function getProcessStepState(index) {
.balance-detail {
display: grid;
- grid-template-columns: minmax(0, 1.26fr) minmax(0, 0.74fr);
- gap: 15px;
+ grid-template-columns: minmax(0, 1.04fr) minmax(0, 0.82fr) 248px;
+ gap: 12px;
min-height: 0;
}
.balance-process,
-.balance-decision {
+.balance-decision,
+.balance-overload-ranking {
position: relative;
min-width: 0;
min-height: 0;
@@ -2183,6 +2239,120 @@ function getProcessStepState(index) {
gap: 12px;
}
+.balance-overload-ranking {
+ display: grid;
+ grid-template-rows: 28px minmax(0, 1fr);
+ gap: 10px;
+}
+
+.balance-rank-list {
+ position: relative;
+ z-index: 1;
+ display: grid;
+ align-content: start;
+ gap: 5px;
+ min-height: 0;
+ overflow: hidden;
+}
+
+.balance-rank-row {
+ position: relative;
+ display: grid;
+ grid-template-columns: 24px 58px minmax(0, 1fr) 50px;
+ align-items: center;
+ gap: 7px;
+ height: 25px;
+ min-width: 0;
+ padding: 0 8px 0 6px;
+ background:
+ linear-gradient(90deg, rgba(48, 220, 255, 0.08), transparent 62%),
+ rgba(4, 33, 46, 0.62);
+ clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 50%, calc(100% - 8px) 100%, 0 100%);
+ box-sizing: border-box;
+
+ &::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ border: 1px solid rgba(48, 220, 255, 0.08);
+ clip-path: inherit;
+ pointer-events: none;
+ }
+
+ > span {
+ position: relative;
+ z-index: 1;
+ width: 20px;
+ height: 18px;
+ color: rgba(126, 251, 246, 0.9);
+ font-family: D-DIN, Arial, sans-serif;
+ font-size: 12px;
+ line-height: 18px;
+ font-weight: 800;
+ text-align: center;
+ background: rgba(126, 251, 246, 0.08);
+ }
+
+ strong {
+ position: relative;
+ z-index: 1;
+ min-width: 0;
+ overflow: hidden;
+ color: rgba(237, 253, 255, 0.9);
+ font-size: 12px;
+ line-height: 18px;
+ font-weight: 800;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ div {
+ position: relative;
+ z-index: 1;
+ height: 5px;
+ overflow: hidden;
+ background: rgba(126, 251, 246, 0.08);
+
+ i {
+ display: block;
+ width: var(--rank-progress);
+ height: 100%;
+ background: linear-gradient(90deg, rgba(126, 251, 246, 0.35), #7efbf6);
+ box-shadow: 0 0 10px rgba(126, 251, 246, 0.22);
+ }
+ }
+
+ em {
+ position: relative;
+ z-index: 1;
+ color: #7efbf6;
+ font-family: D-DIN, Arial, sans-serif;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 800;
+ text-align: right;
+ white-space: nowrap;
+ }
+
+ &.is-warning div i {
+ background: linear-gradient(90deg, rgba(255, 215, 108, 0.32), #ffd76c);
+ box-shadow: 0 0 10px rgba(255, 215, 108, 0.2);
+ }
+
+ &.is-warning em {
+ color: #ffd76c;
+ }
+
+ &.is-alert div i {
+ background: linear-gradient(90deg, rgba(255, 143, 107, 0.3), #ff8f6b);
+ box-shadow: 0 0 10px rgba(255, 143, 107, 0.24);
+ }
+
+ &.is-alert em {
+ color: #ffb15e;
+ }
+}
+
.balance-conclusion {
position: relative;
z-index: 1;
@@ -2542,7 +2712,8 @@ function getProcessStepState(index) {
.balance-dashboard,
.balance-compare,
.balance-process,
-.balance-decision {
+.balance-decision,
+.balance-overload-ranking {
background: linear-gradient(180deg, rgba(7, 48, 62, 0.9), rgba(3, 28, 42, 0.9));
clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 0 100%);
@@ -2607,7 +2778,8 @@ function getProcessStepState(index) {
}
.balance-process,
-.balance-decision {
+.balance-decision,
+.balance-overload-ranking {
padding: 14px 16px;
}
@@ -2712,12 +2884,13 @@ function getProcessStepState(index) {
}
.balance-sheet.is-ready .balance-detail {
- grid-template-columns: minmax(0, 1.18fr) minmax(360px, 0.82fr);
- gap: 14px;
+ grid-template-columns: minmax(0, 1fr) minmax(318px, 0.78fr) 238px;
+ gap: 12px;
}
.balance-sheet.is-ready .balance-process,
-.balance-sheet.is-ready .balance-decision {
+.balance-sheet.is-ready .balance-decision,
+.balance-sheet.is-ready .balance-overload-ranking {
overflow: hidden;
padding: 13px 15px;
}
@@ -2732,6 +2905,11 @@ function getProcessStepState(index) {
gap: 10px;
}
+.balance-sheet.is-ready .balance-overload-ranking {
+ grid-template-rows: 26px minmax(0, 1fr);
+ gap: 8px;
+}
+
.balance-sheet.is-ready .balance-section-title {
height: 26px;
}
@@ -2807,4 +2985,15 @@ function getProcessStepState(index) {
line-height: 18px;
-webkit-line-clamp: 2;
}
+
+.balance-sheet.is-ready .balance-rank-list {
+ gap: 4px;
+}
+
+.balance-sheet.is-ready .balance-rank-row {
+ grid-template-columns: 22px 54px minmax(0, 1fr) 48px;
+ gap: 6px;
+ height: 23px;
+ padding-right: 7px;
+}
diff --git a/src/views/home/index.vue b/src/views/home/index.vue
index 534e51e..50fdd21 100644
--- a/src/views/home/index.vue
+++ b/src/views/home/index.vue
@@ -138,20 +138,27 @@
:disabled="homeVideoRows.length <= 1"
@click="showPrevVideo"
>‹
-
-
-
-
-
加载中
-
{{ videoError }}
-
{{ panel.live.title }}
+
+
+
+
+
+
加载中
+
{{ videoError }}
+
{{ item.live.title }}
+
-
-
-
-
-
加载中
-
{{ pastureVideoError }}
-
{{ currentPastureVideo.title }}
+
+
+
+
+
+
加载中
+
{{ pastureVideoError }}
+
{{ item.live.title }}
+